Merge branch 'topic/misc' into for-linus

This commit is contained in:
Takashi Iwai 2011-01-13 08:37:14 +01:00
commit e38302f782
59 changed files with 2753 additions and 1126 deletions

View File

@ -974,13 +974,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
See hdspm.txt for details.
Module snd-hifier
-----------------
Module for the MediaTek/TempoTec HiFier Fantasia sound card.
This module supports autoprobe and multiple cards.
Module snd-ice1712
------------------
@ -1531,15 +1524,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-oxygen
-----------------
Module for sound cards based on the C-Media CMI8788 chip:
Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
* Asound A-8788
* Asus Xonar DG
* AuzenTech X-Meridian
* AuzenTech X-Meridian 2G
* Bgears b-Enspirer
* Club3D Theatron DTS
* HT-Omega Claro (plus)
* HT-Omega Claro halo (XT)
* Kuroutoshikou CMI8787-HG2PCI
* Razer Barracuda AC-1
* Sondigo Inferno
* TempoTec HiFier Fantasia
* TempoTec HiFier Serenade
This module supports autoprobe and multiple cards.
@ -2006,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
Module snd-virtuoso
-------------------
Module for sound cards based on the Asus AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST
(Deluxe) and Essence STX.
Module for sound cards based on the Asus AV66/AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
HDAV1.3 (Deluxe), and HDAV1.3 Slim.
This module supports autoprobe and multiple cards.

View File

@ -1434,6 +1434,14 @@ S: Supported
F: block/bsg.c
F: include/linux/bsg.h
BT87X AUDIO DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: Documentation/sound/alsa/Bt87x.txt
F: sound/pci/bt87x.c
BT8XXGPIO DRIVER
M: Michael Buesch <mb@bu3sch.de>
W: http://bu3sch.de/btgpio.php
@ -1459,6 +1467,13 @@ S: Maintained
F: Documentation/video4linux/bttv/
F: drivers/media/video/bt8xx/bttv*
C-MEDIA CMI8788 DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/pci/oxygen/
CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
M: David Howells <dhowells@redhat.com>
L: linux-cachefs@redhat.com
@ -2249,6 +2264,13 @@ W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/r82600_edac.c
EDIROL UA-101/UA-1000 DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/usb/misc/ua101.c
EEEPC LAPTOP EXTRAS DRIVER
M: Corentin Chary <corentincj@iksaif.net>
L: acpi4asus-user@lists.sourceforge.net
@ -3393,6 +3415,13 @@ L: linux-serial@vger.kernel.org
S: Maintained
F: drivers/serial/jsm/
K10TEMP HARDWARE MONITORING DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: lm-sensors@lm-sensors.org
S: Maintained
F: Documentation/hwmon/k10temp
F: drivers/hwmon/k10temp.c
K8TEMP HARDWARE MONITORING DRIVER
M: Rudolf Marek <r.marek@assembler.cz>
L: lm-sensors@lm-sensors.org
@ -4409,6 +4438,13 @@ F: drivers/of
F: include/linux/of*.h
K: of_get_property
OPL4 DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/drivers/opl4/
OPROFILE
M: Robert Richter <robert.richter@amd.com>
L: oprofile-list@lists.sf.net
@ -6141,6 +6177,13 @@ S: Maintained
W: http://www.one-eyed-alien.net/~mdharm/linux-usb/
F: drivers/usb/storage/
USB MIDI DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.alsa-project.org/alsa-kernel.git
S: Maintained
F: sound/usb/midi.*
USB OHCI DRIVER
M: David Brownell <dbrownell@users.sourceforge.net>
L: linux-usb@vger.kernel.org

View File

@ -259,6 +259,7 @@ typedef int __bitwise snd_pcm_subformat_t;
#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
typedef int __bitwise snd_pcm_state_t;
@ -334,6 +335,8 @@ typedef int snd_pcm_hw_param_t;
#define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME
#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */
#define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */
#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */
struct snd_interval {
unsigned int min, max;

View File

@ -160,12 +160,14 @@ static inline struct snd_ctl_elem_id *snd_ctl_build_ioff(struct snd_ctl_elem_id
}
/*
* Frequently used control callbacks
* Frequently used control callbacks/helpers
*/
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
unsigned int items, const char *const names[]);
/*
* virtual master control

View File

@ -28,6 +28,7 @@ enum HDSP_IO_Type {
Multiface,
H9652,
H9632,
RPM,
Undefined,
};

View File

@ -31,8 +31,8 @@
/* these minors can still be used for autoloading devices (/dev/aload*) */
#define SNDRV_MINOR_CONTROL 0 /* 0 */
#define SNDRV_MINOR_GLOBAL 1 /* 1 */
#define SNDRV_MINOR_SEQUENCER (SNDRV_MINOR_GLOBAL + 0 * 32)
#define SNDRV_MINOR_TIMER (SNDRV_MINOR_GLOBAL + 1 * 32)
#define SNDRV_MINOR_SEQUENCER 1 /* SNDRV_MINOR_GLOBAL + 0 * 32 */
#define SNDRV_MINOR_TIMER 33 /* SNDRV_MINOR_GLOBAL + 1 * 32 */
#ifndef CONFIG_SND_DYNAMIC_MINORS
/* 2 - 3 (reserved) */

View File

@ -297,6 +297,7 @@ struct snd_pcm_runtime {
unsigned int info;
unsigned int rate_num;
unsigned int rate_den;
unsigned int no_period_wakeup: 1;
/* -- SW params -- */
int tstamp_mode; /* mmap timestamp is updated */

View File

@ -19,8 +19,8 @@
/*
* Let drivers decide whether they want to support given codec from their
* probe method. Drivers have direct access to the struct snd_ac97 structure and may
* decide based on the id field amongst other things.
* probe method. Drivers have direct access to the struct snd_ac97
* structure and may decide based on the id field amongst other things.
*/
static int ac97_bus_match(struct device *dev, struct device_driver *drv)
{

View File

@ -1114,7 +1114,6 @@ static int onyx_i2c_remove(struct i2c_client *client)
of_node_put(onyx->codec.node);
if (onyx->codec_info)
kfree(onyx->codec_info);
i2c_set_clientdata(client, onyx);
kfree(onyx);
return 0;
}

View File

@ -287,10 +287,9 @@ static void ftr_gpio_exit(struct gpio_runtime *rt)
free_irq(linein_detect_irq, &rt->line_in_notify);
if (rt->line_out_notify.gpio_private)
free_irq(lineout_detect_irq, &rt->line_out_notify);
cancel_delayed_work(&rt->headphone_notify.work);
cancel_delayed_work(&rt->line_in_notify.work);
cancel_delayed_work(&rt->line_out_notify.work);
flush_scheduled_work();
cancel_delayed_work_sync(&rt->headphone_notify.work);
cancel_delayed_work_sync(&rt->line_in_notify.work);
cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);
mutex_destroy(&rt->line_out_notify.mutex);

View File

@ -107,10 +107,9 @@ static void pmf_gpio_exit(struct gpio_runtime *rt)
/* make sure no work is pending before freeing
* all things */
cancel_delayed_work(&rt->headphone_notify.work);
cancel_delayed_work(&rt->line_in_notify.work);
cancel_delayed_work(&rt->line_out_notify.work);
flush_scheduled_work();
cancel_delayed_work_sync(&rt->headphone_notify.work);
cancel_delayed_work_sync(&rt->line_in_notify.work);
cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);

View File

@ -1488,7 +1488,7 @@ int snd_ctl_create(struct snd_card *card)
}
/*
* Frequently used control callbacks
* Frequently used control callbacks/helpers
*/
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@ -1513,3 +1513,29 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
/**
* snd_ctl_enum_info - fills the info structure for an enumerated control
* @info: the structure to be filled
* @channels: the number of the control's channels; often one
* @items: the number of control values; also the size of @names
* @names: an array containing the names of all control values
*
* Sets all required fields in @info to their appropriate values.
* If the control's accessibility is not the default (readable and writable),
* the caller has to fill @info->access.
*/
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
unsigned int items, const char *const names[])
{
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = channels;
info->value.enumerated.items = items;
if (info->value.enumerated.item >= items)
info->value.enumerated.item = items - 1;
strlcpy(info->value.enumerated.name,
names[info->value.enumerated.item],
sizeof(info->value.enumerated.name));
return 0;
}
EXPORT_SYMBOL(snd_ctl_enum_info);

View File

@ -453,8 +453,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
if (max < 0)
if (max < 0) {
kfree(save);
return max;
}
last = 1;
}
_end:

View File

@ -373,6 +373,27 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
(unsigned long)new_hw_ptr,
(unsigned long)runtime->hw_ptr_base);
}
if (runtime->no_period_wakeup) {
/*
* Without regular period interrupts, we have to check
* the elapsed time to detect xruns.
*/
jdelta = jiffies - runtime->hw_ptr_jiffies;
if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
goto no_delta_check;
hdelta = jdelta - delta * HZ / runtime->rate;
while (hdelta > runtime->hw_ptr_buffer_jiffies / 2 + 1) {
delta += runtime->buffer_size;
hw_base += runtime->buffer_size;
if (hw_base >= runtime->boundary)
hw_base = 0;
new_hw_ptr = hw_base + pos;
hdelta -= runtime->hw_ptr_buffer_jiffies;
}
goto no_delta_check;
}
/* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) {
hw_ptr_error(substream,
@ -442,6 +463,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
(long)old_hw_ptr);
}
no_delta_check:
if (runtime->status->hw_ptr == new_hw_ptr)
return 0;

View File

@ -422,6 +422,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->info = params->info;
runtime->rate_num = params->rate_num;
runtime->rate_den = params->rate_den;
runtime->no_period_wakeup =
(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
bits = snd_pcm_format_physical_width(runtime->format);
runtime->sample_bits = bits;

View File

@ -32,6 +32,7 @@
#include "seq_timer.h"
#include "seq_system.h"
#include "seq_info.h"
#include <sound/minors.h>
#include <sound/seq_device.h>
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
@ -73,6 +74,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe
module_param(seq_default_timer_resolution, int, 0644);
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER);
MODULE_ALIAS("devname:snd/seq");
/*
* INIT PART
*/

View File

@ -188,14 +188,22 @@ static const struct file_operations snd_fops =
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
static int snd_find_free_minor(void)
static int snd_find_free_minor(int type)
{
int minor;
/* static minors for module auto loading */
if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
return SNDRV_MINOR_SEQUENCER;
if (type == SNDRV_DEVICE_TYPE_TIMER)
return SNDRV_MINOR_TIMER;
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
/* skip minors still used statically for autoloading devices */
if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
minor == SNDRV_MINOR_SEQUENCER)
/* skip static minors still used for module auto loading */
if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
continue;
if (minor == SNDRV_MINOR_SEQUENCER ||
minor == SNDRV_MINOR_TIMER)
continue;
if (!snd_minors[minor])
return minor;
@ -269,7 +277,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
preg->private_data = private_data;
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor();
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])

View File

@ -34,8 +34,8 @@
#include <sound/initval.h>
#include <linux/kmod.h>
#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
#define DEFAULT_TIMER_LIMIT 3
#if defined(CONFIG_SND_HRTIMER) || defined(CONFIG_SND_HRTIMER_MODULE)
#define DEFAULT_TIMER_LIMIT 4
#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
#define DEFAULT_TIMER_LIMIT 2
#else
@ -52,6 +52,9 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
module_param(timer_tstamp_monotonic, int, 0444);
MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
MODULE_ALIAS("devname:snd/timer");
struct snd_timer_user {
struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */

View File

@ -1143,8 +1143,8 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
(resource->start) + 1);
if (ml403_ac97cr->port == NULL) {
snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
"unable to remap memory region (%x to %x)\n",
resource->start, resource->end);
"unable to remap memory region (%pR)\n",
resource);
snd_ml403_ac97cr_free(ml403_ac97cr);
return -EBUSY;
}

View File

@ -57,8 +57,7 @@ static void snd_ak4113_free(struct ak4113 *chip)
{
chip->init = 1; /* don't schedule new work */
mb();
cancel_delayed_work(&chip->work);
flush_scheduled_work();
cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@ -141,7 +140,7 @@ void snd_ak4113_reinit(struct ak4113 *chip)
{
chip->init = 1;
mb();
flush_scheduled_work();
flush_delayed_work_sync(&chip->work);
ak4113_init_regs(chip);
/* bring up statistics / event queing */
chip->init = 0;

View File

@ -67,8 +67,7 @@ static void snd_ak4114_free(struct ak4114 *chip)
{
chip->init = 1; /* don't schedule new work */
mb();
cancel_delayed_work(&chip->work);
flush_scheduled_work();
cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@ -154,7 +153,7 @@ void snd_ak4114_reinit(struct ak4114 *chip)
{
chip->init = 1;
mb();
flush_scheduled_work();
flush_delayed_work_sync(&chip->work);
ak4114_init_regs(chip);
/* bring up statistics / event queing */
chip->init = 0;

View File

@ -209,7 +209,7 @@ config SND_OXYGEN_LIB
tristate
config SND_OXYGEN
tristate "C-Media 8788 (Oxygen)"
tristate "C-Media 8786, 8787, 8788 (Oxygen)"
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@ -217,13 +217,18 @@ config SND_OXYGEN
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
* Asound A-8788
* Asus Xonar DG
* AuzenTech X-Meridian
* AuzenTech X-Meridian 2G
* Bgears b-Enspirer
* Club3D Theatron DTS
* HT-Omega Claro (plus)
* HT-Omega Claro halo (XT)
* Kuroutoshikou CMI8787-HG2PCI
* Razer Barracuda AC-1
* Sondigo Inferno
* TempoTec/MediaTek HiFier Fantasia
* TempoTec/MediaTek HiFier Serenade
To compile this driver as a module, choose M here: the module
will be called snd-oxygen.
@ -578,18 +583,6 @@ config SND_HDSPM
To compile this driver as a module, choose M here: the module
will be called snd-hdspm.
config SND_HIFIER
tristate "TempoTec HiFier Fantasia"
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
help
Say Y here to include support for the MediaTek/TempoTec HiFier
Fantasia sound card.
To compile this driver as a module, choose M here: the module
will be called snd-hifier.
config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
select SND_MPU401_UART
@ -826,8 +819,8 @@ config SND_VIRTUOSO
Say Y here to include support for sound cards based on the
Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
Essence ST (Deluxe), and Essence STX.
Support for the HDAV1.3 (Deluxe) is incomplete; for the
HDAV1.3 Slim and Xense, missing.
Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
for the Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.

View File

@ -1014,8 +1014,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
{
if (ac97) {
#ifdef CONFIG_SND_AC97_POWER_SAVE
cancel_delayed_work(&ac97->power_work);
flush_scheduled_work();
cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_proc_done(ac97);
if (ac97->bus)
@ -2456,8 +2455,7 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
if (ac97->build_ops->suspend)
ac97->build_ops->suspend(ac97);
#ifdef CONFIG_SND_AC97_POWER_SAVE
cancel_delayed_work(&ac97->power_work);
flush_scheduled_work();
cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_powerdown(ac97);
}

View File

@ -1,6 +1,6 @@
/*
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
* Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
* Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de>
*
* Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@ -175,6 +175,7 @@
#include <asm/io.h>
#include <linux/init.h>
#include <linux/bug.h> /* WARN_ONCE */
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/slab.h>
@ -201,14 +202,15 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
/* === Debug settings ===
Further diagnostic functionality than the settings below
does not need to be provided, since one can easily write a bash script
does not need to be provided, since one can easily write a POSIX shell script
to dump the card's I/O ports (those listed in lspci -v -v):
function dump()
dump()
{
local descr=$1; local addr=$2; local count=$3
echo "${descr}: ${count} @ ${addr}:"
dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
2>/dev/null| hexdump -C
}
and then use something like
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
@ -216,14 +218,14 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
possibly within a "while true; do ... sleep 1; done" loop.
Tweaking ports could be done using
VALSTRING="`printf "%02x" $value`"
printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
2>/dev/null
*/
#define DEBUG_MISC 0
#define DEBUG_CALLS 0
#define DEBUG_MIXER 0
#define DEBUG_CODEC 0
#define DEBUG_IO 0
#define DEBUG_TIMER 0
#define DEBUG_GAME 0
#define DEBUG_PM 0
@ -291,19 +293,23 @@ static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
struct snd_azf3328_codec_data {
unsigned long io_base;
struct snd_pcm_substream *substream;
bool running;
const char *name;
};
enum snd_azf3328_codec_type {
/* warning: fixed indices (also used for bitmask checks!) */
AZF_CODEC_PLAYBACK = 0,
AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2,
};
struct snd_azf3328_codec_data {
unsigned long io_base; /* keep first! (avoid offset calc) */
unsigned int dma_base; /* helper to avoid an indirection in hotpath */
spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
struct snd_pcm_substream *substream;
bool running;
enum snd_azf3328_codec_type type;
const char *name;
};
struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */
@ -362,6 +368,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
{
/* Well, strictly spoken, the inb/outb sequence isn't atomic
and would need locking. However we currently don't care
since it potentially complicates matters. */
u8 prev = inb(reg), new;
new = (do_set) ? (prev|mask) : (prev & ~mask);
@ -413,6 +422,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
outl(value, codec->io_base + reg);
}
static inline void
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
unsigned reg, const void *buffer, int count
)
{
unsigned long addr = codec->io_base + reg;
if (count) {
const u32 *buf = buffer;
do {
outl(*buf++, addr);
addr += 4;
} while (--count);
}
}
static inline u32
snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
{
@ -943,38 +967,43 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
}
static void
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
enum snd_azf3328_codec_type codec_type,
snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
{
unsigned long flags;
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00;
u8 freq = 0;
snd_azf3328_dbgcallenter();
switch (bitrate) {
case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
case AZF_FREQ_5512:
/* the AZF3328 names it "5510" for some strange reason */
val |= SOUNDFORMAT_FREQ_5510; break;
case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break;
case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break;
case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break;
case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
#define AZF_FMT_XLATE(in_freq, out_bits) \
do { \
case AZF_FREQ_ ## in_freq: \
freq = SOUNDFORMAT_FREQ_ ## out_bits; \
break; \
} while (0);
AZF_FMT_XLATE(4000, SUSPECTED_4000)
AZF_FMT_XLATE(4800, SUSPECTED_4800)
/* the AZF3328 names it "5510" for some strange reason: */
AZF_FMT_XLATE(5512, 5510)
AZF_FMT_XLATE(6620, 6620)
AZF_FMT_XLATE(8000, 8000)
AZF_FMT_XLATE(9600, 9600)
AZF_FMT_XLATE(11025, 11025)
AZF_FMT_XLATE(13240, SUSPECTED_13240)
AZF_FMT_XLATE(16000, 16000)
AZF_FMT_XLATE(22050, 22050)
AZF_FMT_XLATE(32000, 32000)
default:
snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
/* fall-through */
case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
AZF_FMT_XLATE(44100, 44100)
AZF_FMT_XLATE(48000, 48000)
AZF_FMT_XLATE(66200, SUSPECTED_66200)
#undef AZF_FMT_XLATE
}
/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@ -986,13 +1015,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
val |= freq;
if (channels == 2)
val |= SOUNDFORMAT_FLAG_2CHANNELS;
if (format_width == 16)
val |= SOUNDFORMAT_FLAG_16BIT;
spin_lock_irqsave(&chip->reg_lock, flags);
spin_lock_irqsave(codec->lock, flags);
/* set bitrate/format */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
@ -1004,7 +1035,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
* (FIXME: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */
if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
/* do it for non-capture codecs only */
if (codec->type != AZF_CODEC_CAPTURE)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_RUN_SOMETHING1 |
@ -1014,20 +1046,19 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
DMA_SOMETHING_ELSE
);
spin_unlock_irqrestore(&chip->reg_lock, flags);
spin_unlock_irqrestore(codec->lock, flags);
snd_azf3328_dbgcallleave();
}
static inline void
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
enum snd_azf3328_codec_type codec_type
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
)
{
/* choose lowest frequency for low power consumption.
* While this will cause louder noise due to rather coarse frequency,
* it should never matter since output should always
* get disabled properly when idle anyway. */
snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
}
static void
@ -1101,69 +1132,87 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
/* ...and adjust clock, too
* (reduce noise and power consumption) */
if (!enable)
snd_azf3328_codec_setfmt_lowpower(
chip,
codec_type
);
snd_azf3328_codec_setfmt_lowpower(codec);
codec->running = enable;
}
}
static void
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
enum snd_azf3328_codec_type codec_type,
snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
unsigned long addr,
unsigned int count,
unsigned int size
unsigned int period_bytes,
unsigned int buffer_bytes
)
{
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter();
WARN_ONCE(period_bytes & 1, "odd period length!?\n");
WARN_ONCE(buffer_bytes != 2 * period_bytes,
"missed our input expectations! %u vs. %u\n",
buffer_bytes, period_bytes);
if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */
unsigned long flags, addr_area2;
unsigned long flags;
/* width 32bit (prevent overflow): */
u32 count_areas, lengths;
u32 area_length;
struct codec_setup_io {
u32 dma_start_1;
u32 dma_start_2;
u32 dma_lengths;
} __attribute__((packed)) setup_io;
count_areas = size/2;
addr_area2 = addr+count_areas;
snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
addr, count_areas, addr_area2, count_areas);
area_length = buffer_bytes/2;
count_areas--; /* max. index */
setup_io.dma_start_1 = addr;
setup_io.dma_start_2 = addr+area_length;
snd_azf3328_dbgcodec(
"setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
setup_io.dma_start_1, area_length,
setup_io.dma_start_2, area_length,
period_bytes, buffer_bytes);
/* Hmm, are we really supposed to decrement this by 1??
Most definitely certainly not: configuring full length does
work properly (i.e. likely better), and BTW we
violated possibly differing frame sizes with this...
area_length--; |* max. index *|
*/
/* build combined I/O buffer length word */
lengths = (count_areas << 16) | (count_areas);
spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
addr_area2);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
lengths);
spin_unlock_irqrestore(&chip->reg_lock, flags);
setup_io.dma_lengths = (area_length << 16) | (area_length);
spin_lock_irqsave(codec->lock, flags);
snd_azf3328_codec_outl_multi(
codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
);
spin_unlock_irqrestore(codec->lock, flags);
}
snd_azf3328_dbgcallleave();
}
static int
snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
{
#if 0
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_azf3328_codec_data *codec = runtime->private_data;
#if 0
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
#endif
snd_azf3328_dbgcallenter();
codec->dma_base = runtime->dma_addr;
#if 0
snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
snd_azf3328_codec_setdmaa(codec,
runtime->dma_addr, count, size);
#endif
snd_azf3328_dbgcallleave();
@ -1171,24 +1220,23 @@ snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
}
static int
snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
struct snd_pcm_substream *substream, int cmd)
snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_azf3328_codec_data *codec = runtime->private_data;
int result = 0;
u16 flags1;
bool previously_muted = 0;
bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgcodec("START %s\n", codec->name);
if (is_playback_codec) {
if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
snd_azf3328_mixer_set_mute(
@ -1196,12 +1244,12 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
);
}
snd_azf3328_codec_setfmt(chip, codec_type,
snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
spin_lock(&chip->reg_lock);
spin_lock(codec->lock);
/* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
@ -1211,14 +1259,14 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
/* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
spin_unlock(&chip->reg_lock);
spin_unlock(codec->lock);
snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream)
);
spin_lock(&chip->reg_lock);
spin_lock(codec->lock);
#ifdef WIN9X
/* FIXME: enable playback/recording??? */
flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
@ -1242,10 +1290,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE);
#endif
spin_unlock(&chip->reg_lock);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
spin_unlock(codec->lock);
snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
if (is_playback_codec) {
if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
snd_azf3328_mixer_set_mute(
@ -1258,19 +1306,19 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
/* resume codec if we were active */
spin_lock(&chip->reg_lock);
spin_lock(codec->lock);
if (codec->running)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS
) | DMA_RESUME
);
spin_unlock(&chip->reg_lock);
spin_unlock(codec->lock);
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgcodec("STOP %s\n", codec->name);
if (is_playback_codec) {
if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
snd_azf3328_mixer_set_mute(
@ -1278,7 +1326,7 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
);
}
spin_lock(&chip->reg_lock);
spin_lock(codec->lock);
/* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
@ -1293,10 +1341,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
flags1 &= ~DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
spin_unlock(&chip->reg_lock);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
spin_unlock(codec->lock);
snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
if (is_playback_codec) {
if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
snd_azf3328_mixer_set_mute(
@ -1330,67 +1378,29 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
return result;
}
static int
snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
}
static int
snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
}
static int
snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
{
return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
}
static snd_pcm_uframes_t
snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
enum snd_azf3328_codec_type codec_type
snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
)
{
const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
unsigned long bufptr, result;
const struct snd_azf3328_codec_data *codec =
substream->runtime->private_data;
unsigned long result;
snd_pcm_uframes_t frmres;
#ifdef QUERY_HARDWARE
bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
#else
bufptr = substream->runtime->dma_addr;
#endif
result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */
result -= bufptr;
#ifdef QUERY_HARDWARE
result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
#else
result -= codec->dma_base;
#endif
frmres = bytes_to_frames( substream->runtime, result);
snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
codec->name, result, frmres);
snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
jiffies, codec->name, result, frmres);
return frmres;
}
static snd_pcm_uframes_t
snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
{
return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
}
static snd_pcm_uframes_t
snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
{
return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
}
static snd_pcm_uframes_t
snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
{
return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
}
/******************************************************************/
#ifdef SUPPORT_GAMEPORT
@ -1532,7 +1542,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
}
}
/* trigger next axes sampling, to be evaluated the next time we
/* trigger next sampling of axes, to be evaluated the next time we
* enter this function */
/* for some very, very strange reason we cannot enable
@ -1624,29 +1634,29 @@ snd_azf3328_irq_log_unknown_type(u8 which)
}
static inline void
snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
u8 status
)
{
u8 which;
enum snd_azf3328_codec_type codec_type;
const struct snd_azf3328_codec_data *codec;
const struct snd_azf3328_codec_data *codec = first_codec;
for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT;
++codec_type) {
++codec_type, ++codec) {
/* skip codec if there's no interrupt for it */
if (!(status & (1 << codec_type)))
continue;
codec = &chip->codecs[codec_type];
spin_lock(&chip->reg_lock);
spin_lock(codec->lock);
which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
/* ack all IRQ types immediately */
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
spin_unlock(&chip->reg_lock);
spin_unlock(codec->lock);
if ((chip->pcm[codec_type]) && (codec->substream)) {
if (codec->substream) {
snd_pcm_period_elapsed(codec->substream);
snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
codec->name,
@ -1701,7 +1711,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
}
if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
snd_azf3328_codec_interrupt(chip, status);
snd_azf3328_pcm_interrupt(chip->codecs, status);
if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip);
@ -1789,101 +1799,85 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter();
chip->codecs[codec_type].substream = substream;
codec->substream = substream;
/* same parameters for all our codecs - at least we think so... */
runtime->hw = snd_azf3328_hardware;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
runtime->private_data = codec;
snd_azf3328_dbgcallleave();
return 0;
}
static int
snd_azf3328_playback_open(struct snd_pcm_substream *substream)
snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
}
static int
snd_azf3328_capture_open(struct snd_pcm_substream *substream)
snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
}
static int
snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
}
static int
snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
enum snd_azf3328_codec_type codec_type
snd_azf3328_pcm_close(struct snd_pcm_substream *substream
)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_azf3328_codec_data *codec =
substream->runtime->private_data;
snd_azf3328_dbgcallenter();
chip->codecs[codec_type].substream = NULL;
codec->substream = NULL;
snd_azf3328_dbgcallleave();
return 0;
}
static int
snd_azf3328_playback_close(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
}
static int
snd_azf3328_capture_close(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
}
static int
snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
}
/******************************************************************/
static struct snd_pcm_ops snd_azf3328_playback_ops = {
.open = snd_azf3328_playback_open,
.close = snd_azf3328_playback_close,
.open = snd_azf3328_pcm_playback_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_codec_playback_trigger,
.pointer = snd_azf3328_codec_playback_pointer
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
};
static struct snd_pcm_ops snd_azf3328_capture_ops = {
.open = snd_azf3328_capture_open,
.close = snd_azf3328_capture_close,
.open = snd_azf3328_pcm_capture_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_codec_capture_trigger,
.pointer = snd_azf3328_codec_capture_pointer
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
};
static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
.open = snd_azf3328_i2s_out_open,
.close = snd_azf3328_i2s_out_close,
.open = snd_azf3328_pcm_i2s_out_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare,
.trigger = snd_azf3328_codec_i2s_out_trigger,
.pointer = snd_azf3328_codec_i2s_out_pointer
.prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_pcm_pointer
};
static int __devinit
@ -1966,7 +1960,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
delay = 49; /* minimum time is 49 ticks */
}
snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
snd_azf3328_dbgtimer("setting timer countdown value %d\n", delay);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
@ -2180,6 +2174,7 @@ snd_azf3328_create(struct snd_card *card,
};
u8 dma_init;
enum snd_azf3328_codec_type codec_type;
struct snd_azf3328_codec_data *codec_setup;
*rchip = NULL;
@ -2217,15 +2212,23 @@ snd_azf3328_create(struct snd_card *card,
chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4);
chip->codecs[AZF_CODEC_PLAYBACK].io_base =
chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
chip->codecs[AZF_CODEC_CAPTURE].io_base =
chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
chip->codecs[AZF_CODEC_I2S_OUT].io_base =
chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
codec_setup->lock = &chip->reg_lock;
codec_setup->type = AZF_CODEC_PLAYBACK;
codec_setup->name = "PLAYBACK";
codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
codec_setup->lock = &chip->reg_lock;
codec_setup->type = AZF_CODEC_CAPTURE;
codec_setup->name = "CAPTURE";
codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
codec_setup->lock = &chip->reg_lock;
codec_setup->type = AZF_CODEC_I2S_OUT;
codec_setup->name = "I2S_OUT";
if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) {
@ -2257,15 +2260,15 @@ snd_azf3328_create(struct snd_card *card,
struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type];
/* shutdown codecs to save power */
/* shutdown codecs to reduce power / noise */
/* have ...ctrl_codec_activity() act properly */
codec->running = 1;
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
spin_lock_irq(&chip->reg_lock);
spin_lock_irq(codec->lock);
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init);
spin_unlock_irq(&chip->reg_lock);
spin_unlock_irq(codec->lock);
}
snd_card_set_dev(card, &pci->dev);
@ -2419,6 +2422,7 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
/* same pcm object for playback/capture */
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);

View File

@ -637,15 +637,9 @@ static struct snd_kcontrol_new snd_bt87x_capture_boost = {
static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item > 2)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 3, texts);
}
static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,

View File

@ -2507,14 +2507,12 @@ static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
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 const char *const texts[3] = {
"Line-In", "Rear Output", "Bass Output"
};
return snd_ctl_enum_info(uinfo, 1,
cm->chip_version >= 39 ? 3 : 2, texts);
}
static inline unsigned int get_line_in_mode(struct cmipci *cm)
@ -2564,14 +2562,9 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[2] = { "Mic-In", "Center/LFE Output" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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 const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,

View File

@ -1235,7 +1235,8 @@ static int azx_setup_periods(struct azx *chip,
pos_adj = 0;
} else {
ofs = setup_bdle(substream, azx_dev,
&bdl, ofs, pos_adj, 1);
&bdl, ofs, pos_adj,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
goto error;
}
@ -1247,7 +1248,8 @@ static int azx_setup_periods(struct azx *chip,
period_bytes - pos_adj, 0);
else
ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
period_bytes, 1);
period_bytes,
!substream->runtime->no_period_wakeup);
if (ofs < 0)
goto error;
}
@ -1515,7 +1517,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* No full-resume yet implemented */
/* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START),
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,

View File

@ -10857,6 +10857,9 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
return 0;
}
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg);
/* almost identical with ALC880 parser... */
static int alc882_parse_auto_config(struct hda_codec *codec)
{
@ -10874,7 +10877,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
if (err < 0)
return err;
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (codec->vendor_id == 0x10ec0887)
err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
else
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
@ -17043,7 +17049,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
/* add playback controls from the parsed DAC table */
/* Based on ALC880 version. But ALC861VD has separate,
/* Based on ALC880 version. But ALC861VD and ALC887 have separate,
* different NIDs for mute/unmute switch and volume control */
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)

View File

@ -263,8 +263,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
return;
snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
!spec->vt1708_jack_detectect);
cancel_delayed_work(&spec->vt1708_hp_work);
flush_scheduled_work();
cancel_delayed_work_sync(&spec->vt1708_hp_work);
}

View File

@ -96,6 +96,11 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
break;
case ICE1712_SUBDEVICE_DELTA66E:
tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
ICE1712_DELTA_66E_CS_CHIP_B;
tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
tmp &= ~ICE1712_VX442_CS_DIGITAL;
@ -119,6 +124,9 @@ static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
break;
case ICE1712_SUBDEVICE_DELTA66E:
tmp |= ICE1712_DELTA_66E_CS_CS8427;
break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CS_DIGITAL;
break;
@ -275,6 +283,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
priv->cs_addr = chip << 4;
}
/*
* AK4524 on Delta66 rev E to choose the chip address
*/
static void delta66e_ak4524_lock(struct snd_akm4xxx *ak, int chip)
{
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
struct snd_ice1712 *ice = ak->private_data[0];
snd_ice1712_save_gpio_status(ice);
priv->cs_mask =
priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
ICE1712_DELTA_66E_CS_CHIP_B;
}
/*
* AK4528 on VX442 to choose the chip mask
*/
@ -487,6 +509,29 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta66e __devinitdata = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
.ops = {
.lock = delta66e_ak4524_lock,
.set_rate_val = delta_ak4524_set_rate_val
}
};
static struct snd_ak4xxx_private akm_delta66e_priv __devinitdata = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_66E_DOUT,
.clk_mask = ICE1712_DELTA_66E_CCLK,
.cs_mask = 0,
.cs_addr = 0, /* set later */
.cs_none = 0,
.add_flags = 0,
.mask_flags = 0,
};
static struct snd_akm4xxx akm_delta44 __devinitdata = {
.type = SND_AK4524,
.num_adcs = 4,
@ -644,9 +689,11 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break;
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
break;
default:
snd_BUG();
return -EINVAL;

View File

@ -144,6 +144,17 @@ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
/* M-Audio Delta 66 rev. E definitions.
* Newer revisions of Delta 66 have CS8427 over SPI for
* S/PDIF transceiver instead of CS8404/CS8414. */
/* 0x01 = DFS */
#define ICE1712_DELTA_66E_CCLK 0x02 /* SPI clock */
#define ICE1712_DELTA_66E_DIN 0x04 /* data input */
#define ICE1712_DELTA_66E_DOUT 0x08 /* data output */
#define ICE1712_DELTA_66E_CS_CS8427 0x10 /* chip select, low = CS8427 */
#define ICE1712_DELTA_66E_CS_CHIP_A 0x20 /* AK4524 #0 */
#define ICE1712_DELTA_66E_CS_CHIP_B 0x40 /* AK4524 #1 */
/* Digigram VX442 definitions */
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
#define ICE1712_VX442_DIN 0x04 /* data input */

View File

@ -1,10 +1,8 @@
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
snd-hifier-objs := hifier.o
snd-oxygen-objs := oxygen.o
snd-oxygen-objs := oxygen.o xonar_dg.o
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o

107
sound/pci/oxygen/cs4245.h Normal file
View File

@ -0,0 +1,107 @@
#define CS4245_CHIP_ID 0x01
#define CS4245_POWER_CTRL 0x02
#define CS4245_DAC_CTRL_1 0x03
#define CS4245_ADC_CTRL 0x04
#define CS4245_MCLK_FREQ 0x05
#define CS4245_SIGNAL_SEL 0x06
#define CS4245_PGA_B_CTRL 0x07
#define CS4245_PGA_A_CTRL 0x08
#define CS4245_ANALOG_IN 0x09
#define CS4245_DAC_A_CTRL 0x0a
#define CS4245_DAC_B_CTRL 0x0b
#define CS4245_DAC_CTRL_2 0x0c
#define CS4245_INT_STATUS 0x0d
#define CS4245_INT_MASK 0x0e
#define CS4245_INT_MODE_MSB 0x0f
#define CS4245_INT_MODE_LSB 0x10
/* Chip ID */
#define CS4245_CHIP_PART_MASK 0xf0
#define CS4245_CHIP_REV_MASK 0x0f
/* Power Control */
#define CS4245_FREEZE 0x80
#define CS4245_PDN_MIC 0x08
#define CS4245_PDN_ADC 0x04
#define CS4245_PDN_DAC 0x02
#define CS4245_PDN 0x01
/* DAC Control */
#define CS4245_DAC_FM_MASK 0xc0
#define CS4245_DAC_FM_SINGLE 0x00
#define CS4245_DAC_FM_DOUBLE 0x40
#define CS4245_DAC_FM_QUAD 0x80
#define CS4245_DAC_DIF_MASK 0x30
#define CS4245_DAC_DIF_LJUST 0x00
#define CS4245_DAC_DIF_I2S 0x10
#define CS4245_DAC_DIF_RJUST_16 0x20
#define CS4245_DAC_DIF_RJUST_24 0x30
#define CS4245_RESERVED_1 0x08
#define CS4245_MUTE_DAC 0x04
#define CS4245_DEEMPH 0x02
#define CS4245_DAC_MASTER 0x01
/* ADC Control */
#define CS4245_ADC_FM_MASK 0xc0
#define CS4245_ADC_FM_SINGLE 0x00
#define CS4245_ADC_FM_DOUBLE 0x40
#define CS4245_ADC_FM_QUAD 0x80
#define CS4245_ADC_DIF_MASK 0x10
#define CS4245_ADC_DIF_LJUST 0x00
#define CS4245_ADC_DIF_I2S 0x10
#define CS4245_MUTE_ADC 0x04
#define CS4245_HPF_FREEZE 0x02
#define CS4245_ADC_MASTER 0x01
/* MCLK Frequency */
#define CS4245_MCLK1_MASK 0x70
#define CS4245_MCLK1_SHIFT 4
#define CS4245_MCLK2_MASK 0x07
#define CS4245_MCLK2_SHIFT 0
#define CS4245_MCLK_1 0
#define CS4245_MCLK_1_5 1
#define CS4245_MCLK_2 2
#define CS4245_MCLK_3 3
#define CS4245_MCLK_4 4
/* Signal Selection */
#define CS4245_A_OUT_SEL_MASK 0x60
#define CS4245_A_OUT_SEL_HIZ 0x00
#define CS4245_A_OUT_SEL_DAC 0x20
#define CS4245_A_OUT_SEL_PGA 0x40
#define CS4245_LOOP 0x02
#define CS4245_ASYNCH 0x01
/* Channel B/A PGA Control */
#define CS4245_PGA_GAIN_MASK 0x3f
/* ADC Input Control */
#define CS4245_PGA_SOFT 0x10
#define CS4245_PGA_ZERO 0x08
#define CS4245_SEL_MASK 0x07
#define CS4245_SEL_MIC 0x00
#define CS4245_SEL_INPUT_1 0x01
#define CS4245_SEL_INPUT_2 0x02
#define CS4245_SEL_INPUT_3 0x03
#define CS4245_SEL_INPUT_4 0x04
#define CS4245_SEL_INPUT_5 0x05
#define CS4245_SEL_INPUT_6 0x06
/* DAC Channel A/B Volume Control */
#define CS4245_VOL_MASK 0xff
/* DAC Control 2 */
#define CS4245_DAC_SOFT 0x80
#define CS4245_DAC_ZERO 0x40
#define CS4245_INVERT_DAC 0x20
#define CS4245_INT_ACTIVE_HIGH 0x01
/* Interrupt Status/Mask/Mode */
#define CS4245_ADC_CLK_ERR 0x08
#define CS4245_DAC_CLK_ERR 0x04
#define CS4245_ADC_OVFL 0x02
#define CS4245_ADC_UNDRFL 0x01
#define CS4245_SPI_ADDRESS (0x9e << 16)
#define CS4245_SPI_WRITE (0 << 16)

View File

@ -1,239 +0,0 @@
/*
* C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver 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 driver; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* CMI8788:
*
* SPI 0 -> AK4396
*/
#include <linux/delay.h>
#include <linux/pci.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "ak4396.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("TempoTec HiFier driver");
MODULE_LICENSE("GPL v2");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "card index");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = {
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
};
MODULE_DEVICE_TABLE(pci, hifier_ids);
struct hifier_data {
u8 ak4396_regs[5];
};
static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
{
struct hifier_data *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
AK4396_WRITE | (reg << 8) | value);
data->ak4396_regs[reg] = value;
}
static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
{
struct hifier_data *data = chip->model_data;
if (value != data->ak4396_regs[reg])
ak4396_write(chip, reg, value);
}
static void hifier_registers_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, AK4396_CONTROL_2,
data->ak4396_regs[AK4396_CONTROL_2]);
ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
}
static void hifier_init(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
data->ak4396_regs[AK4396_CONTROL_2] =
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
hifier_registers_init(chip);
snd_component_add(chip->card, "AK4396");
snd_component_add(chip->card, "CS5340");
}
static void hifier_cleanup(struct oxygen *chip)
{
}
static void hifier_resume(struct oxygen *chip)
{
hifier_registers_init(chip);
}
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct hifier_data *data = chip->model_data;
u8 value;
value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
msleep(1); /* wait for the new MCLK to become stable */
if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
ak4396_write(chip, AK4396_CONTROL_1,
AK4396_DIF_24_MSB);
ak4396_write(chip, AK4396_CONTROL_2, value);
ak4396_write(chip, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN);
}
}
static void update_ak4396_volume(struct oxygen *chip)
{
ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
}
static void update_ak4396_mute(struct oxygen *chip)
{
struct hifier_data *data = chip->model_data;
u8 value;
value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
ak4396_write_cached(chip, AK4396_CONTROL_2, value);
}
static void set_cs5340_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
}
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_hifier = {
.shortname = "C-Media CMI8787",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8788",
.init = hifier_init,
.cleanup = hifier_cleanup,
.resume = hifier_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_cs5340_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct hifier_data),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1,
.dac_channels = 2,
.dac_volume_min = 0,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static int __devinit get_hifier_model(struct oxygen *chip,
const struct pci_device_id *id)
{
chip->model = model_hifier;
return 0;
}
static int __devinit hifier_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
++dev;
return -ENOENT;
}
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
hifier_ids, get_hifier_model);
if (err >= 0)
++dev;
return err;
}
static struct pci_driver hifier_driver = {
.name = "CMI8787HiFier",
.id_table = hifier_ids,
.probe = hifier_probe,
.remove = __devexit_p(oxygen_pci_remove),
#ifdef CONFIG_PM
.suspend = oxygen_pci_suspend,
.resume = oxygen_pci_resume,
#endif
};
static int __init alsa_card_hifier_init(void)
{
return pci_register_driver(&hifier_driver);
}
static void __exit alsa_card_hifier_exit(void)
{
pci_unregister_driver(&hifier_driver);
}
module_init(alsa_card_hifier_init)
module_exit(alsa_card_hifier_exit)

View File

@ -20,19 +20,32 @@
/*
* CMI8788:
*
* SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE)
* SPI 3 -> WM8785
* SPI 4 -> 4th AK4396 (back)
* SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE)
* SPI 3 -> WM8785
* SPI 4 -> 4th AK4396 (back)
*
* GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385
* GPIO 8 -> enable headphone amplifier on HT-Omega models
* GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385
*
* X-Meridian models:
* GPIO 4 -> enable extension S/PDIF input
* GPIO 6 -> enable on-board S/PDIF input
*
* Claro models:
* GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input
* GPIO 8 -> enable headphone amplifier
*
* CM9780:
*
* GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* CD_IN <- CD
* MIC_IN <- mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
*/
#include <linux/delay.h>
@ -41,18 +54,22 @@
#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "xonar_dg.h"
#include "ak4396.h"
#include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
MODULE_LICENSE("GPL v2");
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
",{C-Media,CMI8787}"
",{C-Media,CMI8788}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@ -66,24 +83,46 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "enable card");
enum {
MODEL_CMEDIA_REF, /* C-Media's reference design */
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
MODEL_CLARO, /* HT-Omega Claro */
MODEL_CLARO_HALO, /* HT-Omega Claro halo */
MODEL_CMEDIA_REF,
MODEL_MERIDIAN,
MODEL_MERIDIAN_2G,
MODEL_CLARO,
MODEL_CLARO_HALO,
MODEL_FANTASIA,
MODEL_SERENADE,
MODEL_2CH_OUTPUT,
MODEL_HG2PCI,
MODEL_XONAR_DG,
};
static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
/* C-Media's reference design */
{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
/* Asus Xonar DG */
{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
/* PCI 2.0 HD Audio */
{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
/* Kuroutoshikou CMI8787-HG2PCI */
{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
/* TempoTec HiFier Fantasia */
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
/* TempoTec HiFier Serenade */
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
/* AuzenTech X-Meridian */
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
/* AuzenTech X-Meridian 2G */
{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
/* HT-Omega Claro */
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
/* HT-Omega Claro halo */
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
{ }
};
@ -95,9 +134,15 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
#define GPIO_MERIDIAN_DIG_MASK 0x0050
#define GPIO_MERIDIAN_DIG_EXT 0x0010
#define GPIO_MERIDIAN_DIG_BOARD 0x0040
#define GPIO_CLARO_DIG_COAX 0x0040
#define GPIO_CLARO_HP 0x0100
struct generic_data {
unsigned int dacs;
u8 ak4396_regs[4][5];
u16 wm8785_regs[3];
};
@ -148,7 +193,7 @@ static void ak4396_registers_init(struct oxygen *chip)
struct generic_data *data = chip->model_data;
unsigned int i;
for (i = 0; i < 4; ++i) {
for (i = 0; i < data->dacs; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i, AK4396_CONTROL_2,
@ -166,6 +211,7 @@ static void ak4396_init(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
data->dacs = chip->model.dac_channels_pcm / 2;
data->ak4396_regs[0][AK4396_CONTROL_2] =
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_registers_init(chip);
@ -207,6 +253,10 @@ static void generic_init(struct oxygen *chip)
static void meridian_init(struct oxygen *chip)
{
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_MERIDIAN_DIG_MASK);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
ak4396_init(chip);
ak5385_init(chip);
}
@ -220,6 +270,8 @@ static void claro_enable_hp(struct oxygen *chip)
static void claro_init(struct oxygen *chip)
{
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
ak4396_init(chip);
wm8785_init(chip);
claro_enable_hp(chip);
@ -227,11 +279,24 @@ static void claro_init(struct oxygen *chip)
static void claro_halo_init(struct oxygen *chip)
{
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
ak4396_init(chip);
ak5385_init(chip);
claro_enable_hp(chip);
}
static void fantasia_init(struct oxygen *chip)
{
ak4396_init(chip);
snd_component_add(chip->card, "CS5340");
}
static void stereo_output_init(struct oxygen *chip)
{
ak4396_init(chip);
}
static void generic_cleanup(struct oxygen *chip)
{
}
@ -268,6 +333,11 @@ static void claro_resume(struct oxygen *chip)
claro_enable_hp(chip);
}
static void stereo_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
}
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@ -286,7 +356,7 @@ static void set_ak4396_params(struct oxygen *chip,
msleep(1); /* wait for the new MCLK to become stable */
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
for (i = 0; i < 4; ++i) {
for (i = 0; i < data->dacs; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB);
ak4396_write(chip, i, AK4396_CONTROL_2, value);
@ -298,9 +368,10 @@ static void set_ak4396_params(struct oxygen *chip,
static void update_ak4396_volume(struct oxygen *chip)
{
struct generic_data *data = chip->model_data;
unsigned int i;
for (i = 0; i < 4; ++i) {
for (i = 0; i < data->dacs; ++i) {
ak4396_write_cached(chip, i, AK4396_LCH_ATT,
chip->dac_volume[i * 2]);
ak4396_write_cached(chip, i, AK4396_RCH_ATT,
@ -317,7 +388,7 @@ static void update_ak4396_mute(struct oxygen *chip)
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
for (i = 0; i < 4; ++i)
for (i = 0; i < data->dacs; ++i)
ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
}
@ -356,6 +427,10 @@ static void set_ak5385_params(struct oxygen *chip,
value, GPIO_AK5385_DFS_MASK);
}
static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
{
}
static int rolloff_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
@ -363,13 +438,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Sharp Roll-off", "Slow Roll-off"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@ -400,7 +469,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
reg &= ~AK4396_SLOW;
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
if (changed) {
for (i = 0; i < 4; ++i)
for (i = 0; i < data->dacs; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
}
mutex_unlock(&chip->mutex);
@ -421,13 +490,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"None", "High-pass Filter"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@ -466,6 +529,100 @@ static const struct snd_kcontrol_new hpf_control = {
.put = hpf_put,
};
static int meridian_dig_source_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[2] = { "On-board", "Extension" };
return snd_ctl_enum_info(info, 1, 2, names);
}
static int claro_dig_source_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[2] = { "Optical", "Coaxial" };
return snd_ctl_enum_info(info, 1, 2, names);
}
static int meridian_dig_source_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
value->value.enumerated.item[0] =
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
GPIO_MERIDIAN_DIG_EXT);
return 0;
}
static int claro_dig_source_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
value->value.enumerated.item[0] =
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
GPIO_CLARO_DIG_COAX);
return 0;
}
static int meridian_dig_source_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
u16 old_reg, new_reg;
int changed;
mutex_lock(&chip->mutex);
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
if (value->value.enumerated.item[0] == 0)
new_reg |= GPIO_MERIDIAN_DIG_BOARD;
else
new_reg |= GPIO_MERIDIAN_DIG_EXT;
changed = new_reg != old_reg;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
mutex_unlock(&chip->mutex);
return changed;
}
static int claro_dig_source_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
u16 old_reg, new_reg;
int changed;
mutex_lock(&chip->mutex);
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
if (value->value.enumerated.item[0])
new_reg |= GPIO_CLARO_DIG_COAX;
changed = new_reg != old_reg;
if (changed)
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
mutex_unlock(&chip->mutex);
return changed;
}
static const struct snd_kcontrol_new meridian_dig_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Source Capture Enum",
.info = meridian_dig_source_info,
.get = meridian_dig_source_get,
.put = meridian_dig_source_put,
};
static const struct snd_kcontrol_new claro_dig_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Source Capture Enum",
.info = claro_dig_source_info,
.get = claro_dig_source_get,
.put = claro_dig_source_put,
};
static int generic_mixer_init(struct oxygen *chip)
{
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
@ -484,6 +641,81 @@ static int generic_wm8785_mixer_init(struct oxygen *chip)
return 0;
}
static int meridian_mixer_init(struct oxygen *chip)
{
int err;
err = generic_mixer_init(chip);
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&meridian_dig_source_control, chip));
if (err < 0)
return err;
return 0;
}
static int claro_mixer_init(struct oxygen *chip)
{
int err;
err = generic_wm8785_mixer_init(chip);
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&claro_dig_source_control, chip));
if (err < 0)
return err;
return 0;
}
static int claro_halo_mixer_init(struct oxygen *chip)
{
int err;
err = generic_mixer_init(chip);
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&claro_dig_source_control, chip));
if (err < 0)
return err;
return 0;
}
static void dump_ak4396_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct generic_data *data = chip->model_data;
unsigned int dac, i;
for (dac = 0; dac < data->dacs; ++dac) {
snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
for (i = 0; i < 5; ++i)
snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
}
snd_iprintf(buffer, "\n");
}
static void dump_wm8785_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct generic_data *data = chip->model_data;
unsigned int i;
snd_iprintf(buffer, "\nWM8785:");
for (i = 0; i < 3; ++i)
snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
snd_iprintf(buffer, "\n");
}
static void dump_oxygen_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
dump_ak4396_registers(chip, buffer);
dump_wm8785_registers(chip, buffer);
}
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = {
@ -494,11 +726,11 @@ static const struct oxygen_model model_generic = {
.mixer_init = generic_wm8785_mixer_init,
.cleanup = generic_cleanup,
.resume = generic_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
.dump_registers = dump_oxygen_registers,
.dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data),
.device_config = PLAYBACK_0_TO_I2S |
@ -508,11 +740,14 @@ static const struct oxygen_model model_generic = {
CAPTURE_1_FROM_SPDIF |
CAPTURE_2_FROM_AC97_1 |
AC97_CD_INPUT,
.dac_channels = 8,
.dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 0,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@ -520,42 +755,87 @@ static const struct oxygen_model model_generic = {
static int __devinit get_oxygen_model(struct oxygen *chip,
const struct pci_device_id *id)
{
static const char *const names[] = {
[MODEL_MERIDIAN] = "AuzenTech X-Meridian",
[MODEL_MERIDIAN_2G] = "AuzenTech X-Meridian 2G",
[MODEL_CLARO] = "HT-Omega Claro",
[MODEL_CLARO_HALO] = "HT-Omega Claro halo",
[MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
[MODEL_SERENADE] = "TempoTec HiFier Serenade",
[MODEL_HG2PCI] = "CMI8787-HG2PCI",
};
chip->model = model_generic;
switch (id->driver_data) {
case MODEL_MERIDIAN:
case MODEL_MERIDIAN_2G:
chip->model.init = meridian_init;
chip->model.mixer_init = generic_mixer_init;
chip->model.mixer_init = meridian_mixer_init;
chip->model.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params;
chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
if (id->driver_data == MODEL_MERIDIAN)
chip->model.device_config |= AC97_CD_INPUT;
break;
case MODEL_CLARO:
chip->model.init = claro_init;
chip->model.mixer_init = claro_mixer_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
break;
case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init;
chip->model.mixer_init = generic_mixer_init;
chip->model.mixer_init = claro_halo_mixer_init;
chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
chip->model.set_adc_params = set_ak5385_params;
chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
break;
case MODEL_FANTASIA:
case MODEL_SERENADE:
case MODEL_2CH_OUTPUT:
case MODEL_HG2PCI:
chip->model.shortname = "C-Media CMI8787";
chip->model.chip = "CMI8787";
if (id->driver_data == MODEL_FANTASIA)
chip->model.init = fantasia_init;
else
chip->model.init = stereo_output_init;
chip->model.resume = stereo_resume;
chip->model.mixer_init = generic_mixer_init;
chip->model.set_adc_params = set_no_params;
chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF;
if (id->driver_data == MODEL_FANTASIA) {
chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
}
chip->model.dac_channels_pcm = 2;
chip->model.dac_channels_mixer = 2;
break;
case MODEL_XONAR_DG:
chip->model = model_xonar_dg;
break;
}
if (id->driver_data == MODEL_MERIDIAN ||
id->driver_data == MODEL_MERIDIAN_2G ||
id->driver_data == MODEL_CLARO_HALO) {
chip->model.misc_flags = OXYGEN_MISC_MIDI;
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
}
if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
chip->model.shortname = names[id->driver_data];
return 0;
}

View File

@ -16,6 +16,10 @@
#define PCM_AC97 5
#define PCM_COUNT 6
#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \
(MCLK_##f_double << 2) | \
(MCLK_##f_quad << 4))
#define OXYGEN_IO_SIZE 0x100
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
@ -35,6 +39,7 @@
#define MIDI_OUTPUT 0x0800
#define MIDI_INPUT 0x1000
#define AC97_CD_INPUT 0x2000
#define AC97_FMIC_SWITCH 0x4000
enum {
CONTROL_SPDIF_PCM,
@ -65,6 +70,7 @@ struct snd_pcm_hardware;
struct snd_pcm_hw_params;
struct snd_kcontrol_new;
struct snd_rawmidi;
struct snd_info_buffer;
struct oxygen;
struct oxygen_model {
@ -79,8 +85,6 @@ struct oxygen_model {
void (*resume)(struct oxygen *chip);
void (*pcm_hardware_filter)(unsigned int channel,
struct snd_pcm_hardware *hardware);
unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
struct snd_pcm_hw_params *hw_params);
void (*set_dac_params)(struct oxygen *chip,
struct snd_pcm_hw_params *params);
void (*set_adc_params)(struct oxygen *chip,
@ -92,15 +96,19 @@ struct oxygen_model {
void (*uart_input)(struct oxygen *chip);
void (*ac97_switch)(struct oxygen *chip,
unsigned int reg, unsigned int mute);
void (*dump_registers)(struct oxygen *chip,
struct snd_info_buffer *buffer);
const unsigned int *dac_tlv;
unsigned long private_data;
size_t model_data_size;
unsigned int device_config;
u8 dac_channels;
u8 dac_channels_pcm;
u8 dac_channels_mixer;
u8 dac_volume_min;
u8 dac_volume_max;
u8 misc_flags;
u8 function_flags;
u8 dac_mclks;
u8 adc_mclks;
u16 dac_i2s_format;
u16 adc_i2s_format;
};
@ -121,7 +129,6 @@ struct oxygen {
u8 pcm_running;
u8 dac_routing;
u8 spdif_playback_enable;
u8 revision;
u8 has_ac97_0;
u8 has_ac97_1;
u32 spdif_bits;
@ -167,8 +174,6 @@ void oxygen_update_spdif_source(struct oxygen *chip);
/* oxygen_pcm.c */
int oxygen_pcm_init(struct oxygen *chip);
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
struct snd_pcm_hw_params *hw_params);
/* oxygen_io.c */

View File

@ -197,11 +197,11 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
{
unsigned int count;
/* should not need more than 7.68 us (24 * 320 ns) */
/* should not need more than 30.72 us (24 * 1.28 us) */
count = 10;
while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
&& count > 0) {
udelay(1);
udelay(4);
--count;
}

View File

@ -202,7 +202,13 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
struct oxygen *chip = entry->private_data;
int i, j;
snd_iprintf(buffer, "CMI8788\n\n");
switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) {
case OXYGEN_PACKAGE_ID_8786: i = '6'; break;
case OXYGEN_PACKAGE_ID_8787: i = '7'; break;
case OXYGEN_PACKAGE_ID_8788: i = '8'; break;
default: i = '?'; break;
}
snd_iprintf(buffer, "CMI878%c:\n", i);
for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; ++j)
@ -212,7 +218,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
if (mutex_lock_interruptible(&chip->mutex) < 0)
return;
if (chip->has_ac97_0) {
snd_iprintf(buffer, "\nAC97\n");
snd_iprintf(buffer, "\nAC97:\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
@ -222,7 +228,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
}
}
if (chip->has_ac97_1) {
snd_iprintf(buffer, "\nAC97 2\n");
snd_iprintf(buffer, "\nAC97 2:\n");
for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2)
@ -232,13 +238,15 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
}
}
mutex_unlock(&chip->mutex);
if (chip->model.dump_registers)
chip->model.dump_registers(chip, buffer);
}
static void oxygen_proc_init(struct oxygen *chip)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
if (!snd_card_proc_new(chip->card, "oxygen", &entry))
snd_info_set_text_ops(entry, chip, oxygen_proc_read);
}
#else
@ -262,7 +270,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
*/
subdevice = oxygen_read_eeprom(chip, 2);
/* use default ID if EEPROM is missing */
if (subdevice == 0xffff)
if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
subdevice = 0x8788;
/*
* We use only the subsystem device ID for searching because it is
@ -364,12 +372,7 @@ static void oxygen_init(struct oxygen *chip)
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
chip->spdif_pcm_bits = chip->spdif_bits;
if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
chip->revision = 2;
else
chip->revision = 1;
if (chip->revision == 1)
if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
oxygen_set_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
@ -406,28 +409,40 @@ static void oxygen_init(struct oxygen *chip)
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
OXYGEN_RATE_48000 |
chip->model.dac_i2s_format |
OXYGEN_I2S_MCLK(chip->model.dac_mclks) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
OXYGEN_RATE_48000 |
chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
OXYGEN_I2S_MASTER |
OXYGEN_I2S_MUTE_MCLK);
if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
CAPTURE_2_FROM_I2S_2))
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
OXYGEN_RATE_48000 |
chip->model.adc_i2s_format |
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
else
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
OXYGEN_I2S_MASTER |
OXYGEN_I2S_MUTE_MCLK);
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
OXYGEN_I2S_MASTER |
OXYGEN_I2S_MUTE_MCLK);
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK);
@ -557,7 +572,8 @@ static void oxygen_card_free(struct snd_card *card)
oxygen_shutdown(chip);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
flush_scheduled_work();
flush_work_sync(&chip->spdif_input_bits_work);
flush_work_sync(&chip->gpio_work);
chip->model.cleanup(chip);
kfree(chip->model_data);
mutex_destroy(&chip->mutex);
@ -648,8 +664,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
strcpy(card->driver, chip->model.chip);
strcpy(card->shortname, chip->model.shortname);
sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
chip->model.longname, chip->revision, chip->addr, chip->irq);
sprintf(card->longname, "%s at %#lx, irq %i",
chip->model.longname, chip->addr, chip->irq);
strcpy(card->mixername, chip->model.chip);
snd_component_add(card, chip->model.chip);
@ -733,7 +749,8 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
spin_unlock_irq(&chip->reg_lock);
synchronize_irq(chip->irq);
flush_scheduled_work();
flush_work_sync(&chip->spdif_input_bits_work);
flush_work_sync(&chip->gpio_work);
chip->interrupt_mask = saved_interrupt_mask;
pci_disable_device(pci);

View File

@ -31,7 +31,7 @@ static int dac_volume_info(struct snd_kcontrol *ctl,
struct oxygen *chip = ctl->private_data;
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = chip->model.dac_channels;
info->count = chip->model.dac_channels_mixer;
info->value.integer.min = chip->model.dac_volume_min;
info->value.integer.max = chip->model.dac_volume_max;
return 0;
@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
unsigned int i;
mutex_lock(&chip->mutex);
for (i = 0; i < chip->model.dac_channels; ++i)
for (i = 0; i < chip->model.dac_channels_mixer; ++i)
value->value.integer.value[i] = chip->dac_volume[i];
mutex_unlock(&chip->mutex);
return 0;
@ -59,7 +59,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
changed = 0;
mutex_lock(&chip->mutex);
for (i = 0; i < chip->model.dac_channels; ++i)
for (i = 0; i < chip->model.dac_channels_mixer; ++i)
if (value->value.integer.value[i] != chip->dac_volume[i]) {
chip->dac_volume[i] = value->value.integer.value[i];
changed = 1;
@ -97,6 +97,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
return changed;
}
static unsigned int upmix_item_count(struct oxygen *chip)
{
if (chip->model.dac_channels_pcm < 8)
return 2;
else if (chip->model.update_center_lfe_mix)
return 5;
else
return 3;
}
static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{
static const char *const names[5] = {
@ -107,15 +117,9 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"Front+Surround+Center/LFE+Back",
};
struct oxygen *chip = ctl->private_data;
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
unsigned int count = upmix_item_count(chip);
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = count;
if (info->value.enumerated.item >= count)
info->value.enumerated.item = count - 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, count, names);
}
static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@ -188,7 +192,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
unsigned int count = upmix_item_count(chip);
int changed;
if (value->value.enumerated.item[0] >= count)
@ -430,30 +434,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,
return 0;
}
static int spdif_loopback_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
static int spdif_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
u32 bit = ctl->private_value;
value->value.integer.value[0] =
!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
& OXYGEN_SPDIF_LOOPBACK);
!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);
return 0;
}
static int spdif_loopback_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
u32 bit = ctl->private_value;
u32 oldreg, newreg;
int changed;
spin_lock_irq(&chip->reg_lock);
oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (value->value.integer.value[0])
newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
newreg = oldreg | bit;
else
newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
newreg = oldreg & ~bit;
changed = newreg != oldreg;
if (changed)
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
@ -644,6 +649,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
return change;
}
static int mic_fmic_source_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[] = { "Mic Jack", "Front Panel" };
return snd_ctl_enum_info(info, 1, 2, names);
}
static int mic_fmic_source_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
mutex_lock(&chip->mutex);
value->value.enumerated.item[0] =
!!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC);
mutex_unlock(&chip->mutex);
return 0;
}
static int mic_fmic_source_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
u16 oldreg, newreg;
int change;
mutex_lock(&chip->mutex);
oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK);
if (value->value.enumerated.item[0])
newreg = oldreg | CM9780_FMIC2MIC;
else
newreg = oldreg & ~CM9780_FMIC2MIC;
change = newreg != oldreg;
if (change)
oxygen_write_ac97(chip, 0, CM9780_JACK, newreg);
mutex_unlock(&chip->mutex);
return change;
}
static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
@ -791,8 +836,17 @@ static const struct snd_kcontrol_new spdif_input_controls[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
.info = snd_ctl_boolean_mono_info,
.get = spdif_loopback_get,
.put = spdif_loopback_put,
.get = spdif_bit_switch_get,
.put = spdif_bit_switch_put,
.private_value = OXYGEN_SPDIF_LOOPBACK,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Validity Check ",CAPTURE,SWITCH),
.info = snd_ctl_boolean_mono_info,
.get = spdif_bit_switch_get,
.put = spdif_bit_switch_put,
.private_value = OXYGEN_SPDIF_SPDVALID,
},
};
@ -908,6 +962,13 @@ static const struct snd_kcontrol_new ac97_controls[] = {
AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Source Capture Enum",
.info = mic_fmic_source_info,
.get = mic_fmic_source_get,
.put = mic_fmic_source_put,
},
AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@ -970,7 +1031,10 @@ static int add_controls(struct oxygen *chip,
continue;
}
if (!strcmp(template.name, "Stereo Upmixing") &&
chip->model.dac_channels == 2)
chip->model.dac_channels_pcm == 2)
continue;
if (!strcmp(template.name, "Mic Source Capture Enum") &&
!(chip->model.device_config & AC97_FMIC_SWITCH))
continue;
if (!strncmp(template.name, "CD Capture ", 11) &&
!(chip->model.device_config & AC97_CD_INPUT))

View File

@ -39,7 +39,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
@ -65,7 +66,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 |
@ -91,7 +93,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START,
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@ -140,7 +143,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->hw.rate_min = 44100;
break;
case PCM_MULTICH:
runtime->hw.channels_max = chip->model.dac_channels;
runtime->hw.channels_max = chip->model.dac_channels_pcm;
break;
}
if (chip->model.pcm_hardware_filter)
@ -271,17 +274,6 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
}
}
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
unsigned int channel,
struct snd_pcm_hw_params *hw_params)
{
if (params_rate(hw_params) <= 96000)
return OXYGEN_I2S_MCLK_256;
else
return OXYGEN_I2S_MCLK_128;
}
EXPORT_SYMBOL(oxygen_default_i2s_mclk);
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
{
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
@ -341,6 +333,26 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static u16 get_mclk(struct oxygen *chip, unsigned int channel,
struct snd_pcm_hw_params *params)
{
unsigned int mclks, shift;
if (channel == PCM_MULTICH)
mclks = chip->model.dac_mclks;
else
mclks = chip->model.adc_mclks;
if (params_rate(params) <= 48000)
shift = 0;
else if (params_rate(params) <= 96000)
shift = 2;
else
shift = 4;
return OXYGEN_I2S_MCLK(mclks >> shift);
}
static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
@ -357,8 +369,8 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
OXYGEN_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) |
chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
chip->model.adc_i2s_format |
get_mclk(chip, PCM_A, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@ -393,9 +405,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) |
chip->model.get_i2s_mclk(chip, PCM_B,
hw_params) |
chip->model.adc_i2s_format |
get_mclk(chip, PCM_B, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@ -476,8 +487,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) |
chip->model.dac_i2s_format |
chip->model.get_i2s_mclk(chip, PCM_MULTICH,
hw_params) |
get_mclk(chip, PCM_MULTICH, hw_params) |
oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
@ -530,7 +540,10 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
chip->interrupt_mask |= channel_mask;
if (substream->runtime->no_period_wakeup)
chip->interrupt_mask &= ~channel_mask;
else
chip->interrupt_mask |= channel_mask;
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock);
return 0;

View File

@ -139,9 +139,11 @@
#define OXYGEN_I2S_FORMAT_I2S 0x0000
#define OXYGEN_I2S_FORMAT_LJUST 0x0008
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
#define OXYGEN_I2S_MCLK_128 0x0000
#define OXYGEN_I2S_MCLK_256 0x0010
#define OXYGEN_I2S_MCLK_512 0x0020
#define OXYGEN_I2S_MCLK_SHIFT 4
#define MCLK_128 0
#define MCLK_256 1
#define MCLK_512 2
#define OXYGEN_I2S_MCLK(f) (((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)
#define OXYGEN_I2S_BITS_MASK 0x00c0
#define OXYGEN_I2S_BITS_16 0x0000
#define OXYGEN_I2S_BITS_20 0x0040
@ -238,11 +240,11 @@
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
#define OXYGEN_SPI_DATA_LENGTH_2 0x00
#define OXYGEN_SPI_DATA_LENGTH_3 0x02
#define OXYGEN_SPI_CLOCK_MASK 0xc0
#define OXYGEN_SPI_CLOCK_MASK 0x0c
#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
#define OXYGEN_SPI_CLOCK_320 0x40
#define OXYGEN_SPI_CLOCK_640 0x80
#define OXYGEN_SPI_CLOCK_1280 0xc0
#define OXYGEN_SPI_CLOCK_320 0x04
#define OXYGEN_SPI_CLOCK_640 0x08
#define OXYGEN_SPI_CLOCK_1280 0x0c
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
#define OXYGEN_SPI_CODEC_SHIFT 4
#define OXYGEN_SPI_CEN_MASK 0x80

View File

@ -24,6 +24,8 @@ void xonar_init_ext_power(struct oxygen *chip);
void xonar_init_cs53x1(struct oxygen *chip);
void xonar_set_cs53x1_params(struct oxygen *chip,
struct snd_pcm_hw_params *params);
#define XONAR_GPIO_BIT_INVERT (1 << 16)
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value);
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,

View File

@ -22,29 +22,28 @@
*
* CMI8788:
*
* I²C <-> CS4398 (front)
* <-> CS4362A (surround, center/LFE, back)
* I²C <-> CS4398 (addr 1001111) (front)
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
*
* GPI 0 <- external power present (DX only)
* GPI 0 <- external power present (DX only)
*
* GPIO 0 -> enable output to speakers
* GPIO 1 -> enable front panel I/O
* GPIO 2 -> M0 of CS5361
* GPIO 3 -> M1 of CS5361
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* CS4398:
*
* AD0 <- 1
* AD1 <- 1
*
* CS4362A:
*
* AD0 <- 0
* GPIO 0 -> enable output to speakers
* GPIO 1 -> route output to front panel
* GPIO 2 -> M0 of CS5361
* GPIO 3 -> M1 of CS5361
* GPIO 6 -> ?
* GPIO 7 -> ?
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* CM9780:
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* MIC_IN <- mic
* FMIC_IN <- front mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
*/
#include <linux/pci.h>
@ -63,6 +62,7 @@
#define GPI_EXT_POWER 0x01
#define GPIO_D1_OUTPUT_ENABLE 0x0001
#define GPIO_D1_FRONT_PANEL 0x0002
#define GPIO_D1_MAGIC 0x00c0
#define GPIO_D1_INPUT_ROUTE 0x0100
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
@ -169,12 +169,12 @@ static void xonar_d1_init(struct oxygen *chip)
cs43xx_registers_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
GPIO_D1_FRONT_PANEL |
GPIO_D1_MAGIC |
GPIO_D1_INPUT_ROUTE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
xonar_init_cs53x1(chip);
xonar_enable_output(chip);
@ -284,7 +284,7 @@ static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
static const struct snd_kcontrol_new front_panel_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Front Panel Switch",
.name = "Front Panel Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put,
@ -298,13 +298,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Fast Roll-off", "Slow Roll-off"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@ -380,6 +374,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
return 0;
}
static void dump_cs4362a_registers(struct xonar_cs43xx *data,
struct snd_info_buffer *buffer)
{
unsigned int i;
snd_iprintf(buffer, "\nCS4362A:");
for (i = 1; i <= 14; ++i)
snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
snd_iprintf(buffer, "\n");
}
static void dump_d1_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct xonar_cs43xx *data = chip->model_data;
unsigned int i;
snd_iprintf(buffer, "\nCS4398: 7?");
for (i = 2; i <= 8; ++i)
snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
snd_iprintf(buffer, "\n");
dump_cs4362a_registers(data, buffer);
}
static const struct oxygen_model model_xonar_d1 = {
.longname = "Asus Virtuoso 100",
.chip = "AV200",
@ -388,22 +406,26 @@ static const struct oxygen_model model_xonar_d1 = {
.cleanup = xonar_d1_cleanup,
.suspend = xonar_d1_suspend,
.resume = xonar_d1_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_cs43xx_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_cs43xx_volume,
.update_dac_mute = update_cs43xx_mute,
.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
.ac97_switch = xonar_d1_line_mic_ac97_switch,
.dump_registers = dump_d1_registers,
.dac_tlv = cs4362a_db_scale,
.model_data_size = sizeof(struct xonar_cs43xx),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels = 8,
CAPTURE_0_FROM_I2S_2 |
AC97_FMIC_SWITCH,
.dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 127 - 60,
.dac_volume_max = 127,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};

572
sound/pci/oxygen/xonar_dg.c Normal file
View File

@ -0,0 +1,572 @@
/*
* card driver for the Xonar DG
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
*
* This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.
*
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Xonar DG
* --------
*
* CMI8788:
*
* SPI 0 -> CS4245
*
* GPIO 3 <- ?
* GPIO 4 <- headphone detect
* GPIO 5 -> route input jack to line-in (0) or mic-in (1)
* GPIO 6 -> route input jack to line-in (0) or mic-in (1)
* GPIO 7 -> enable rear headphone amp
* GPIO 8 -> enable output to speakers
*
* CS4245:
*
* input 1 <- aux
* input 2 <- front mic
* input 4 <- line/mic
* aux out -> front panel headphones
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "xonar_dg.h"
#include "cs4245.h"
#define GPIO_MAGIC 0x0008
#define GPIO_HP_DETECT 0x0010
#define GPIO_INPUT_ROUTE 0x0060
#define GPIO_HP_REAR 0x0080
#define GPIO_OUTPUT_ENABLE 0x0100
struct dg {
unsigned int output_sel;
s8 input_vol[4][2];
unsigned int input_sel;
u8 hp_vol_att;
u8 cs4245_regs[0x11];
};
static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
{
struct dg *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_3 |
OXYGEN_SPI_CLOCK_1280 |
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
CS4245_SPI_ADDRESS |
CS4245_SPI_WRITE |
(value << 8) | reg);
data->cs4245_regs[reg] = value;
}
static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
{
struct dg *data = chip->model_data;
if (value != data->cs4245_regs[reg])
cs4245_write(chip, reg, value);
}
static void cs4245_registers_init(struct oxygen *chip)
{
struct dg *data = chip->model_data;
cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
cs4245_write(chip, CS4245_DAC_CTRL_1,
data->cs4245_regs[CS4245_DAC_CTRL_1]);
cs4245_write(chip, CS4245_ADC_CTRL,
data->cs4245_regs[CS4245_ADC_CTRL]);
cs4245_write(chip, CS4245_SIGNAL_SEL,
data->cs4245_regs[CS4245_SIGNAL_SEL]);
cs4245_write(chip, CS4245_PGA_B_CTRL,
data->cs4245_regs[CS4245_PGA_B_CTRL]);
cs4245_write(chip, CS4245_PGA_A_CTRL,
data->cs4245_regs[CS4245_PGA_A_CTRL]);
cs4245_write(chip, CS4245_ANALOG_IN,
data->cs4245_regs[CS4245_ANALOG_IN]);
cs4245_write(chip, CS4245_DAC_A_CTRL,
data->cs4245_regs[CS4245_DAC_A_CTRL]);
cs4245_write(chip, CS4245_DAC_B_CTRL,
data->cs4245_regs[CS4245_DAC_B_CTRL]);
cs4245_write(chip, CS4245_DAC_CTRL_2,
CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
cs4245_write(chip, CS4245_INT_MASK, 0);
cs4245_write(chip, CS4245_POWER_CTRL, 0);
}
static void cs4245_init(struct oxygen *chip)
{
struct dg *data = chip->model_data;
data->cs4245_regs[CS4245_DAC_CTRL_1] =
CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
data->cs4245_regs[CS4245_ADC_CTRL] =
CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
data->cs4245_regs[CS4245_SIGNAL_SEL] =
CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
data->cs4245_regs[CS4245_ANALOG_IN] =
CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
cs4245_registers_init(chip);
snd_component_add(chip->card, "CS4245");
}
static void dg_output_enable(struct oxygen *chip)
{
msleep(2500);
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
}
static void dg_init(struct oxygen *chip)
{
struct dg *data = chip->model_data;
data->output_sel = 0;
data->input_sel = 3;
data->hp_vol_att = 2 * 16;
cs4245_init(chip);
oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_MAGIC | GPIO_HP_DETECT);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_INPUT_ROUTE | GPIO_HP_REAR);
dg_output_enable(chip);
}
static void dg_cleanup(struct oxygen *chip)
{
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
}
static void dg_suspend(struct oxygen *chip)
{
dg_cleanup(chip);
}
static void dg_resume(struct oxygen *chip)
{
cs4245_registers_init(chip);
dg_output_enable(chip);
}
static void set_cs4245_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct dg *data = chip->model_data;
u8 value;
value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
if (params_rate(params) <= 50000)
value |= CS4245_DAC_FM_SINGLE;
else if (params_rate(params) <= 100000)
value |= CS4245_DAC_FM_DOUBLE;
else
value |= CS4245_DAC_FM_QUAD;
cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
}
static void set_cs4245_adc_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct dg *data = chip->model_data;
u8 value;
value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
if (params_rate(params) <= 50000)
value |= CS4245_ADC_FM_SINGLE;
else if (params_rate(params) <= 100000)
value |= CS4245_ADC_FM_DOUBLE;
else
value |= CS4245_ADC_FM_QUAD;
cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
}
static int output_switch_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[3] = {
"Speakers", "Headphones", "FP Headphones"
};
return snd_ctl_enum_info(info, 1, 3, names);
}
static int output_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
mutex_lock(&chip->mutex);
value->value.enumerated.item[0] = data->output_sel;
mutex_unlock(&chip->mutex);
return 0;
}
static int output_switch_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
u8 reg;
int changed;
if (value->value.enumerated.item[0] > 2)
return -EINVAL;
mutex_lock(&chip->mutex);
changed = value->value.enumerated.item[0] != data->output_sel;
if (changed) {
data->output_sel = value->value.enumerated.item[0];
reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
~CS4245_A_OUT_SEL_MASK;
reg |= data->output_sel == 2 ?
CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
data->output_sel ? data->hp_vol_att : 0);
cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
data->output_sel ? data->hp_vol_att : 0);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
data->output_sel == 1 ? GPIO_HP_REAR : 0,
GPIO_HP_REAR);
}
mutex_unlock(&chip->mutex);
return changed;
}
static int hp_volume_offset_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[3] = {
"< 64 ohms", "64-150 ohms", "150-300 ohms"
};
return snd_ctl_enum_info(info, 1, 3, names);
}
static int hp_volume_offset_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
mutex_lock(&chip->mutex);
if (data->hp_vol_att > 2 * 7)
value->value.enumerated.item[0] = 0;
else if (data->hp_vol_att > 0)
value->value.enumerated.item[0] = 1;
else
value->value.enumerated.item[0] = 2;
mutex_unlock(&chip->mutex);
return 0;
}
static int hp_volume_offset_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
s8 att;
int changed;
if (value->value.enumerated.item[0] > 2)
return -EINVAL;
att = atts[value->value.enumerated.item[0]];
mutex_lock(&chip->mutex);
changed = att != data->hp_vol_att;
if (changed) {
data->hp_vol_att = att;
if (data->output_sel) {
cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
}
}
mutex_unlock(&chip->mutex);
return changed;
}
static int input_vol_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = 2;
info->value.integer.min = 2 * -12;
info->value.integer.max = 2 * 12;
return 0;
}
static int input_vol_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
unsigned int idx = ctl->private_value;
mutex_lock(&chip->mutex);
value->value.integer.value[0] = data->input_vol[idx][0];
value->value.integer.value[1] = data->input_vol[idx][1];
mutex_unlock(&chip->mutex);
return 0;
}
static int input_vol_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
unsigned int idx = ctl->private_value;
int changed = 0;
if (value->value.integer.value[0] < 2 * -12 ||
value->value.integer.value[0] > 2 * 12 ||
value->value.integer.value[1] < 2 * -12 ||
value->value.integer.value[1] > 2 * 12)
return -EINVAL;
mutex_lock(&chip->mutex);
changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
data->input_vol[idx][1] != value->value.integer.value[1];
if (changed) {
data->input_vol[idx][0] = value->value.integer.value[0];
data->input_vol[idx][1] = value->value.integer.value[1];
if (idx == data->input_sel) {
cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
data->input_vol[idx][0]);
cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
data->input_vol[idx][1]);
}
}
mutex_unlock(&chip->mutex);
return changed;
}
static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
static int input_sel_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info)
{
static const char *const names[4] = {
"Mic", "Aux", "Front Mic", "Line"
};
return snd_ctl_enum_info(info, 1, 4, names);
}
static int input_sel_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
mutex_lock(&chip->mutex);
value->value.enumerated.item[0] = data->input_sel;
mutex_unlock(&chip->mutex);
return 0;
}
static int input_sel_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
static const u8 sel_values[4] = {
CS4245_SEL_MIC,
CS4245_SEL_INPUT_1,
CS4245_SEL_INPUT_2,
CS4245_SEL_INPUT_4
};
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
int changed;
if (value->value.enumerated.item[0] > 3)
return -EINVAL;
mutex_lock(&chip->mutex);
changed = value->value.enumerated.item[0] != data->input_sel;
if (changed) {
data->input_sel = value->value.enumerated.item[0];
cs4245_write(chip, CS4245_ANALOG_IN,
(data->cs4245_regs[CS4245_ANALOG_IN] &
~CS4245_SEL_MASK) |
sel_values[data->input_sel]);
cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
data->input_vol[data->input_sel][0]);
cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
data->input_vol[data->input_sel][1]);
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
data->input_sel ? 0 : GPIO_INPUT_ROUTE,
GPIO_INPUT_ROUTE);
}
mutex_unlock(&chip->mutex);
return changed;
}
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{
static const char *const names[2] = { "Active", "Frozen" };
return snd_ctl_enum_info(info, 1, 2, names);
}
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
value->value.enumerated.item[0] =
!!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
return 0;
}
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct dg *data = chip->model_data;
u8 reg;
int changed;
mutex_lock(&chip->mutex);
reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
if (value->value.enumerated.item[0])
reg |= CS4245_HPF_FREEZE;
changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
if (changed)
cs4245_write(chip, CS4245_ADC_CTRL, reg);
mutex_unlock(&chip->mutex);
return changed;
}
#define INPUT_VOLUME(xname, index) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.info = input_vol_info, \
.get = input_vol_get, \
.put = input_vol_put, \
.tlv = { .p = cs4245_pga_db_scale }, \
.private_value = index, \
}
static const struct snd_kcontrol_new dg_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Output Playback Enum",
.info = output_switch_info,
.get = output_switch_get,
.put = output_switch_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphones Impedance Playback Enum",
.info = hp_volume_offset_info,
.get = hp_volume_offset_get,
.put = hp_volume_offset_put,
},
INPUT_VOLUME("Mic Capture Volume", 0),
INPUT_VOLUME("Aux Capture Volume", 1),
INPUT_VOLUME("Front Mic Capture Volume", 2),
INPUT_VOLUME("Line Capture Volume", 3),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = input_sel_info,
.get = input_sel_get,
.put = input_sel_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC High-pass Filter Capture Enum",
.info = hpf_info,
.get = hpf_get,
.put = hpf_put,
},
};
static int dg_control_filter(struct snd_kcontrol_new *template)
{
if (!strncmp(template->name, "Master Playback ", 16))
return 1;
return 0;
}
static int dg_mixer_init(struct oxygen *chip)
{
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
err = snd_ctl_add(chip->card,
snd_ctl_new1(&dg_controls[i], chip));
if (err < 0)
return err;
}
return 0;
}
static void dump_cs4245_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct dg *data = chip->model_data;
unsigned int i;
snd_iprintf(buffer, "\nCS4245:");
for (i = 1; i <= 0x10; ++i)
snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
snd_iprintf(buffer, "\n");
}
struct oxygen_model model_xonar_dg = {
.shortname = "Xonar DG",
.longname = "C-Media Oxygen HD Audio",
.chip = "CMI8786",
.init = dg_init,
.control_filter = dg_control_filter,
.mixer_init = dg_mixer_init,
.cleanup = dg_cleanup,
.suspend = dg_suspend,
.resume = dg_resume,
.set_dac_params = set_cs4245_dac_params,
.set_adc_params = set_cs4245_adc_params,
.dump_registers = dump_cs4245_registers,
.model_data_size = sizeof(struct dg),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels_pcm = 6,
.dac_channels_mixer = 0,
.function_flags = OXYGEN_FUNCTION_SPI,
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};

View File

@ -0,0 +1,8 @@
#ifndef XONAR_DG_H_INCLUDED
#define XONAR_DG_H_INCLUDED
#include "oxygen.h"
extern struct oxygen_model model_xonar_dg;
#endif

View File

@ -1,5 +1,5 @@
/*
* helper functions for HDMI models (Xonar HDAV1.3)
* helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*

View File

@ -104,9 +104,10 @@ int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value;
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
value->value.integer.value[0] =
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
return 0;
}
@ -115,12 +116,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value;
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
u16 old_bits, new_bits;
int changed;
spin_lock_irq(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
if (value->value.integer.value[0])
if (!!value->value.integer.value[0] ^ invert)
new_bits = old_bits | bit;
else
new_bits = old_bits & ~bit;

View File

@ -22,20 +22,26 @@
*
* CMI8788:
*
* SPI 0 -> 1st PCM1796 (front)
* SPI 1 -> 2nd PCM1796 (surround)
* SPI 2 -> 3rd PCM1796 (center/LFE)
* SPI 4 -> 4th PCM1796 (back)
* SPI 0 -> 1st PCM1796 (front)
* SPI 1 -> 2nd PCM1796 (surround)
* SPI 2 -> 3rd PCM1796 (center/LFE)
* SPI 4 -> 4th PCM1796 (back)
*
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 5 <- external power present (D2X only)
* GPIO 7 -> ALT
* GPIO 8 -> enable output to speakers
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 5 <- external power present (D2X only)
* GPIO 7 -> ALT
* GPIO 8 -> enable output to speakers
*
* CM9780:
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* VIDEO_IN <- CD
* FMIC_IN <- mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*/
/*
@ -44,52 +50,53 @@
*
* CMI8788:
*
* I²C <-> PCM1796 (front)
* I²C <-> PCM1796 (addr 1001100) (front)
*
* GPI 0 <- external power present
* GPI 0 <- external power present
*
* GPIO 0 -> enable output to speakers
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
* GPIO 0 -> enable HDMI (0) or speaker (1) output
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 4 <- daughterboard detection
* GPIO 5 <- daughterboard detection
* GPIO 6 -> ?
* GPIO 7 -> ?
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* TXD -> HDMI controller
* RXD <- HDMI controller
*
* PCM1796 front: AD1,0 <- 0,0
* UART <-> HDMI controller
*
* CM9780:
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* CD_IN <- CD
* MIC_IN <- mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*
* no daughterboard
* ----------------
*
* GPIO 4 <- 1
* GPIO 4 <- 1
*
* H6 daughterboard
* ----------------
*
* GPIO 4 <- 0
* GPIO 5 <- 0
* GPIO 4 <- 0
* GPIO 5 <- 0
*
* I²C <-> PCM1796 (surround)
* <-> PCM1796 (center/LFE)
* <-> PCM1796 (back)
*
* PCM1796 surround: AD1,0 <- 0,1
* PCM1796 center/LFE: AD1,0 <- 1,0
* PCM1796 back: AD1,0 <- 1,1
* I²C <-> PCM1796 (addr 1001101) (surround)
* <-> PCM1796 (addr 1001110) (center/LFE)
* <-> PCM1796 (addr 1001111) (back)
*
* unknown daughterboard
* ---------------------
*
* GPIO 4 <- 0
* GPIO 5 <- 1
* GPIO 4 <- 0
* GPIO 5 <- 1
*
* I²C <-> CS4362A (surround, center/LFE, back)
*
* CS4362A: AD0 <- 0
* I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)
*/
/*
@ -98,32 +105,35 @@
*
* CMI8788:
*
* I²C <-> PCM1792A
* <-> CS2000 (ST only)
* I²C <-> PCM1792A (addr 1001100)
* <-> CS2000 (addr 1001110) (ST only)
*
* ADC1 MCLK -> REF_CLK of CS2000 (ST only)
* ADC1 MCLK -> REF_CLK of CS2000 (ST only)
*
* GPI 0 <- external power present (STX only)
* GPI 0 <- external power present (STX only)
*
* GPIO 0 -> enable output to speakers
* GPIO 1 -> route HP to front panel (0) or rear jack (1)
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 7 -> route output to speaker jacks (0) or HP (1)
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
* GPIO 0 -> enable output to speakers
* GPIO 1 -> route HP to front panel (0) or rear jack (1)
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 4 <- daughterboard detection
* GPIO 5 <- daughterboard detection
* GPIO 6 -> ?
* GPIO 7 -> route output to speaker jacks (0) or HP (1)
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* PCM1792A:
*
* AD1,0 <- 0,0
* SCK <- CLK_OUT of CS2000 (ST only)
*
* CS2000:
*
* AD0 <- 0
* SCK <- CLK_OUT of CS2000 (ST only)
*
* CM9780:
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* MIC_IN <- mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
*
* H6 daughterboard
* ----------------
@ -133,15 +143,39 @@
*/
/*
* Xonar HDAV1.3 Slim
* ------------------
* Xonar Xense
* -----------
*
* CMI8788:
*
* GPIO 1 -> enable output
* I²C <-> PCM1796 (addr 1001100) (front)
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
* <-> CS2000 (addr 1001110)
*
* TXD -> HDMI controller
* RXD <- HDMI controller
* ADC1 MCLK -> REF_CLK of CS2000
*
* GPI 0 <- external power present
*
* GPIO 0 -> enable output
* GPIO 1 -> route HP to front panel (0) or rear jack (1)
* GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381
* GPIO 4 -> enable output
* GPIO 5 -> enable output
* GPIO 6 -> ?
* GPIO 7 -> route output to HP (0) or speaker (1)
* GPIO 8 -> route input jack to mic-in (0) or line-in (1)
*
* CM9780:
*
* LINE_OUT -> input of ADC
*
* AUX_IN <- aux
* VIDEO_IN <- ?
* FMIC_IN <- mic
*
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
* GPO 1 -> route mic-in from input jack (0) or front panel header (1)
*/
#include <linux/pci.h>
@ -150,6 +184,7 @@
#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@ -167,12 +202,14 @@
#define GPIO_INPUT_ROUTE 0x0100
#define GPIO_HDAV_OUTPUT_ENABLE 0x0001
#define GPIO_HDAV_MAGIC 0x00c0
#define GPIO_DB_MASK 0x0030
#define GPIO_DB_H6 0x0000
#define GPIO_ST_OUTPUT_ENABLE 0x0001
#define GPIO_ST_HP_REAR 0x0002
#define GPIO_ST_MAGIC 0x0040
#define GPIO_ST_HP 0x0080
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */
@ -186,11 +223,12 @@ struct xonar_pcm179x {
unsigned int dacs;
u8 pcm1796_regs[4][5];
unsigned int current_rate;
bool os_128;
bool h6;
bool hp_active;
s8 hp_gain_offset;
bool has_cs2000;
u8 cs2000_fun_cfg_1;
u8 cs2000_regs[0x1f];
bool broken_i2c;
};
struct xonar_hdav {
@ -249,16 +287,14 @@ static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
struct xonar_pcm179x *data = chip->model_data;
oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
if (reg == CS2000_FUN_CFG_1)
data->cs2000_fun_cfg_1 = value;
data->cs2000_regs[reg] = value;
}
static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
{
struct xonar_pcm179x *data = chip->model_data;
if (reg != CS2000_FUN_CFG_1 ||
value != data->cs2000_fun_cfg_1)
if (value != data->cs2000_regs[reg])
cs2000_write(chip, reg, value);
}
@ -268,6 +304,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
unsigned int i;
s8 gain_offset;
msleep(1);
gain_offset = data->hp_active ? data->hp_gain_offset : 0;
for (i = 0; i < data->dacs; ++i) {
/* set ATLD before ATL/ATR */
@ -282,6 +319,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
pcm1796_write(chip, i, 20,
data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
pcm1796_write(chip, i, 21, 0);
gain_offset = 0;
}
}
@ -290,10 +328,11 @@ static void pcm1796_init(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
PCM1796_FLT_SHARP | PCM1796_ATS_1;
data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
data->pcm1796_regs[0][20 - PCM1796_REG_BASE] =
data->h6 ? PCM1796_OS_64 : PCM1796_OS_128;
pcm1796_registers_init(chip);
data->current_rate = 48000;
}
@ -339,18 +378,20 @@ static void xonar_hdav_init(struct oxygen *chip)
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_FAST);
OXYGEN_2WIRE_SPEED_STANDARD);
data->pcm179x.generic.anti_pop_delay = 100;
data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
data->pcm179x.dacs = chip->model.dac_channels_mixer / 2;
data->pcm179x.h6 = chip->model.dac_channels_mixer > 2;
pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_HDAV_MAGIC | GPIO_INPUT_ROUTE);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
xonar_init_cs53x1(chip);
@ -367,7 +408,7 @@ static void xonar_st_init_i2c(struct oxygen *chip)
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_FAST);
OXYGEN_2WIRE_SPEED_STANDARD);
}
static void xonar_st_init_common(struct oxygen *chip)
@ -375,13 +416,14 @@ static void xonar_st_init_common(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
data->dacs = chip->model.private_data ? 4 : 1;
data->dacs = chip->model.dac_channels_mixer / 2;
data->hp_gain_offset = 2*-18;
pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
GPIO_ST_MAGIC | GPIO_ST_HP);
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
@ -410,9 +452,11 @@ static void cs2000_registers_init(struct oxygen *chip)
cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
cs2000_write(chip, CS2000_FUN_CFG_1,
data->cs2000_regs[CS2000_FUN_CFG_1]);
cs2000_write(chip, CS2000_FUN_CFG_2, 0);
cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
msleep(3); /* PLL lock delay */
}
static void xonar_st_init(struct oxygen *chip)
@ -420,13 +464,18 @@ static void xonar_st_init(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
data->generic.anti_pop_delay = 100;
data->h6 = chip->model.dac_channels_mixer > 2;
data->has_cs2000 = 1;
data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
data->broken_i2c = true;
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
OXYGEN_RATE_48000 |
OXYGEN_I2S_FORMAT_I2S |
OXYGEN_I2S_MCLK(data->h6 ? MCLK_256 : MCLK_512) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
xonar_st_init_i2c(chip);
cs2000_registers_init(chip);
@ -507,44 +556,16 @@ static void xonar_st_resume(struct oxygen *chip)
xonar_stx_resume(chip);
}
static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
{
struct xonar_pcm179x *data = chip->model_data;
if (rate <= 32000)
return OXYGEN_I2S_MCLK_512;
else if (rate <= 48000 && data->os_128)
return OXYGEN_I2S_MCLK_512;
else if (rate <= 96000)
return OXYGEN_I2S_MCLK_256;
else
return OXYGEN_I2S_MCLK_128;
}
static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
unsigned int channel,
struct snd_pcm_hw_params *params)
{
if (channel == PCM_MULTICH)
return mclk_from_rate(chip, params_rate(params));
else
return oxygen_default_i2s_mclk(chip, channel, params);
}
static void update_pcm1796_oversampling(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
unsigned int i;
u8 reg;
if (data->current_rate <= 32000)
if (data->current_rate <= 48000 && !data->h6)
reg = PCM1796_OS_128;
else if (data->current_rate <= 48000 && data->os_128)
reg = PCM1796_OS_128;
else if (data->current_rate <= 96000 || data->os_128)
reg = PCM1796_OS_64;
else
reg = PCM1796_OS_32;
reg = PCM1796_OS_64;
for (i = 0; i < data->dacs; ++i)
pcm1796_write_cached(chip, i, 20, reg);
}
@ -554,6 +575,7 @@ static void set_pcm1796_params(struct oxygen *chip,
{
struct xonar_pcm179x *data = chip->model_data;
msleep(1);
data->current_rate = params_rate(params);
update_pcm1796_oversampling(chip);
}
@ -570,6 +592,7 @@ static void update_pcm1796_volume(struct oxygen *chip)
+ gain_offset);
pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
+ gain_offset);
gain_offset = 0;
}
}
@ -579,7 +602,7 @@ static void update_pcm1796_mute(struct oxygen *chip)
unsigned int i;
u8 value;
value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
if (chip->dac_mute)
value |= PCM1796_MUTE;
for (i = 0; i < data->dacs; ++i)
@ -592,45 +615,35 @@ static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
u8 rate_mclk, reg;
switch (rate) {
/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
case 32000:
rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
case 64000:
rate_mclk = OXYGEN_RATE_32000;
break;
case 44100:
if (data->os_128)
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
else
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
break;
default: /* 48000 */
if (data->os_128)
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
else
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
break;
case 64000:
rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
break;
case 88200:
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
break;
case 96000:
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
break;
case 176400:
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
rate_mclk = OXYGEN_RATE_44100;
break;
default:
case 48000:
case 96000:
case 192000:
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
rate_mclk = OXYGEN_RATE_48000;
break;
}
if (rate <= 96000 && (rate > 48000 || data->h6)) {
rate_mclk |= OXYGEN_I2S_MCLK(MCLK_256);
reg = CS2000_REF_CLK_DIV_1;
} else {
rate_mclk |= OXYGEN_I2S_MCLK(MCLK_512);
reg = CS2000_REF_CLK_DIV_2;
}
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
reg = CS2000_REF_CLK_DIV_1;
else
reg = CS2000_REF_CLK_DIV_2;
cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
msleep(3); /* PLL lock delay */
}
static void set_st_params(struct oxygen *chip,
@ -665,13 +678,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Sharp Roll-off", "Slow Roll-off"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int rolloff_get(struct snd_kcontrol *ctl,
@ -719,57 +726,13 @@ static const struct snd_kcontrol_new rolloff_control = {
.put = rolloff_put,
};
static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{
static const char *const names[2] = { "64x", "128x" };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
}
static int os_128_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
value->value.enumerated.item[0] = data->os_128;
return 0;
}
static int os_128_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value)
{
struct oxygen *chip = ctl->private_data;
struct xonar_pcm179x *data = chip->model_data;
int changed;
mutex_lock(&chip->mutex);
changed = value->value.enumerated.item[0] != data->os_128;
if (changed) {
data->os_128 = value->value.enumerated.item[0];
if (data->has_cs2000)
update_cs2000_rate(chip, data->current_rate);
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
mclk_from_rate(chip, data->current_rate),
OXYGEN_I2S_MCLK_MASK);
update_pcm1796_oversampling(chip);
}
mutex_unlock(&chip->mutex);
return changed;
}
static const struct snd_kcontrol_new os_128_control = {
static const struct snd_kcontrol_new hdav_hdmi_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Oversampling Playback Enum",
.info = os_128_info,
.get = os_128_get,
.put = os_128_put,
.name = "HDMI Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put,
.private_value = GPIO_HDAV_OUTPUT_ENABLE | XONAR_GPIO_BIT_INVERT,
};
static int st_output_switch_info(struct snd_kcontrol *ctl,
@ -779,13 +742,7 @@ static int st_output_switch_info(struct snd_kcontrol *ctl,
"Speakers", "Headphones", "FP Headphones"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item >= 3)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 3, names);
}
static int st_output_switch_get(struct snd_kcontrol *ctl,
@ -840,13 +797,7 @@ static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
"< 64 ohms", "64-300 ohms", "300-600 ohms"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item > 2)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 3, names);
}
static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
@ -928,16 +879,25 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
return 0;
}
static int xonar_st_h6_control_filter(struct snd_kcontrol_new *template)
{
if (!strncmp(template->name, "Master Playback ", 16))
/* no volume/mute, as I²C to the third DAC does not work */
return 1;
return 0;
}
static int add_pcm1796_controls(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
int err;
err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
if (err < 0)
return err;
if (!data->broken_i2c) {
err = snd_ctl_add(chip->card,
snd_ctl_new1(&rolloff_control, chip));
if (err < 0)
return err;
}
return 0;
}
@ -956,7 +916,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)
static int xonar_hdav_mixer_init(struct oxygen *chip)
{
return add_pcm1796_controls(chip);
int err;
err = snd_ctl_add(chip->card, snd_ctl_new1(&hdav_hdmi_control, chip));
if (err < 0)
return err;
err = add_pcm1796_controls(chip);
if (err < 0)
return err;
return 0;
}
static int xonar_st_mixer_init(struct oxygen *chip)
@ -976,6 +944,45 @@ static int xonar_st_mixer_init(struct oxygen *chip)
return 0;
}
static void dump_pcm1796_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct xonar_pcm179x *data = chip->model_data;
unsigned int dac, i;
for (dac = 0; dac < data->dacs; ++dac) {
snd_iprintf(buffer, "\nPCM1796 %u:", dac + 1);
for (i = 0; i < 5; ++i)
snd_iprintf(buffer, " %02x",
data->pcm1796_regs[dac][i]);
}
snd_iprintf(buffer, "\n");
}
static void dump_cs2000_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct xonar_pcm179x *data = chip->model_data;
unsigned int i;
if (data->has_cs2000) {
snd_iprintf(buffer, "\nCS2000:\n00: ");
for (i = 1; i < 0x10; ++i)
snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
snd_iprintf(buffer, "\n10:");
for (i = 0x10; i < 0x1f; ++i)
snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
snd_iprintf(buffer, "\n");
}
}
static void dump_st_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
dump_pcm1796_registers(chip, buffer);
dump_cs2000_registers(chip, buffer);
}
static const struct oxygen_model model_xonar_d2 = {
.longname = "Asus Virtuoso 200",
.chip = "AV200",
@ -985,11 +992,11 @@ static const struct oxygen_model model_xonar_d2 = {
.cleanup = xonar_d2_cleanup,
.suspend = xonar_d2_suspend,
.resume = xonar_d2_resume,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_pcm1796_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S |
@ -999,13 +1006,16 @@ static const struct oxygen_model model_xonar_d2 = {
MIDI_OUTPUT |
MIDI_INPUT |
AC97_CD_INPUT,
.dac_channels = 8,
.dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@ -1018,25 +1028,28 @@ static const struct oxygen_model model_xonar_hdav = {
.suspend = xonar_hdav_suspend,
.resume = xonar_hdav_resume,
.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_hdav_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.uart_input = xonar_hdmi_uart_input,
.ac97_switch = xonar_line_mic_ac97_switch,
.dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_hdav),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF,
.dac_channels = 8,
.dac_channels_pcm = 8,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@ -1048,22 +1061,26 @@ static const struct oxygen_model model_xonar_st = {
.cleanup = xonar_st_cleanup,
.suspend = xonar_st_suspend,
.resume = xonar_st_resume,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_st_params,
.set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute,
.ac97_switch = xonar_line_mic_ac97_switch,
.dump_registers = dump_st_registers,
.dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2,
.dac_channels = 2,
CAPTURE_0_FROM_I2S_2 |
AC97_FMIC_SWITCH,
.dac_channels_pcm = 2,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@ -1089,7 +1106,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break;
case GPIO_DB_H6:
chip->model.shortname = "Xonar HDAV1.3+H6";
chip->model.private_data = 1;
chip->model.dac_channels_mixer = 8;
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break;
}
break;
@ -1102,8 +1120,10 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break;
case GPIO_DB_H6:
chip->model.shortname = "Xonar ST+H6";
chip->model.dac_channels = 8;
chip->model.private_data = 1;
chip->model.control_filter = xonar_st_h6_control_filter;
chip->model.dac_channels_pcm = 8;
chip->model.dac_channels_mixer = 8;
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break;
}
break;
@ -1114,9 +1134,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params;
break;
case 0x835e:
snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
return -ENODEV;
default:
return -EINVAL;
}

View File

@ -1,5 +1,5 @@
/*
* card driver for models with WM8776/WM8766 DACs (Xonar DS)
* card driver for models with WM8776/WM8766 DACs (Xonar DS/HDAV1.3 Slim)
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
@ -22,26 +22,48 @@
*
* CMI8788:
*
* SPI 0 -> WM8766 (surround, center/LFE, back)
* SPI 1 -> WM8776 (front, input)
* SPI 0 -> WM8766 (surround, center/LFE, back)
* SPI 1 -> WM8776 (front, input)
*
* GPIO 4 <- headphone detect, 0 = plugged
* GPIO 6 -> route input jack to mic-in (0) or line-in (1)
* GPIO 7 -> enable output to front L/R speaker channels
* GPIO 8 -> enable output to other speaker channels and front panel headphone
* GPIO 4 <- headphone detect, 0 = plugged
* GPIO 6 -> route input jack to mic-in (0) or line-in (1)
* GPIO 7 -> enable output to front L/R speaker channels
* GPIO 8 -> enable output to other speaker channels and front panel headphone
*
* WM8766:
* WM8776:
*
* input 1 <- line
* input 2 <- mic
* input 3 <- front mic
* input 4 <- aux
* input 1 <- line
* input 2 <- mic
* input 3 <- front mic
* input 4 <- aux
*/
/*
* Xonar HDAV1.3 Slim
* ------------------
*
* CMI8788:
*
* I²C <-> WM8776 (addr 0011010)
*
* GPIO 0 -> disable HDMI output
* GPIO 1 -> enable HP output
* GPIO 6 -> firmware EEPROM I²C clock
* GPIO 7 <-> firmware EEPROM I²C data
*
* UART <-> HDMI controller
*
* WM8776:
*
* input 1 <- mic
* input 2 <- aux
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@ -55,6 +77,13 @@
#define GPIO_DS_OUTPUT_FRONTLR 0x0080
#define GPIO_DS_OUTPUT_ENABLE 0x0100
#define GPIO_SLIM_HDMI_DISABLE 0x0001
#define GPIO_SLIM_OUTPUT_ENABLE 0x0002
#define GPIO_SLIM_FIRMWARE_CLK 0x0040
#define GPIO_SLIM_FIRMWARE_DATA 0x0080
#define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
#define LC_CONTROL_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000
@ -66,19 +95,37 @@ struct xonar_wm87x6 {
struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13];
struct snd_jack *hp_jack;
struct xonar_hdmi hdmi;
};
static void wm8776_write(struct oxygen *chip,
unsigned int reg, unsigned int value)
static void wm8776_write_spi(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
struct xonar_wm87x6 *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 |
(1 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
}
static void wm8776_write_i2c(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
(reg << 1) | (value >> 8), value);
}
static void wm8776_write(struct oxygen *chip,
unsigned int reg, unsigned int value)
{
struct xonar_wm87x6 *data = chip->model_data;
if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
OXYGEN_FUNCTION_SPI)
wm8776_write_spi(chip, reg, value);
else
wm8776_write_i2c(chip, reg, value);
if (reg < ARRAY_SIZE(data->wm8776_regs)) {
if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
value &= ~WM8776_UPDATE;
@ -245,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
snd_component_add(chip->card, "WM8766");
}
static void xonar_hdav_slim_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
data->generic.anti_pop_delay = 300;
data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
wm8776_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
GPIO_SLIM_HDMI_DISABLE |
GPIO_SLIM_FIRMWARE_CLK |
GPIO_SLIM_FIRMWARE_DATA);
xonar_hdmi_init(chip, &data->hdmi);
xonar_enable_output(chip);
snd_component_add(chip->card, "WM8776");
}
static void xonar_ds_cleanup(struct oxygen *chip)
{
xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0);
}
static void xonar_hdav_slim_cleanup(struct oxygen *chip)
{
xonar_hdmi_cleanup(chip);
xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0);
msleep(2);
}
static void xonar_ds_suspend(struct oxygen *chip)
{
xonar_ds_cleanup(chip);
}
static void xonar_hdav_slim_suspend(struct oxygen *chip)
{
xonar_hdav_slim_cleanup(chip);
}
static void xonar_ds_resume(struct oxygen *chip)
{
wm8776_registers_init(chip);
@ -264,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
xonar_ds_handle_hp_jack(chip);
}
static void xonar_hdav_slim_resume(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
wm8776_registers_init(chip);
xonar_hdmi_resume(chip, &data->hdmi);
xonar_enable_output(chip);
}
static void wm8776_adc_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
@ -278,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,
}
}
static void xonar_hdav_slim_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware)
{
wm8776_adc_hardware_filter(channel, hardware);
xonar_hdmi_pcm_hardware_filter(channel, hardware);
}
static void set_wm87x6_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
@ -294,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
}
static void set_hdav_slim_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
struct xonar_wm87x6 *data = chip->model_data;
xonar_set_hdmi_params(chip, &data->hdmi, params);
}
static void update_wm8776_volume(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@ -473,11 +577,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
const char *const *names;
max = (ctl->private_value >> 12) & 0xf;
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = max + 1;
if (info->value.enumerated.item > max)
info->value.enumerated.item = max;
switch ((ctl->private_value >> 24) & 0x1f) {
case WM8776_ALCCTRL2:
names = hld;
@ -501,8 +600,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
default:
return -ENXIO;
}
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, max + 1, names);
}
static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
@ -759,13 +857,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl,
static const char *const names[3] = {
"None", "Peak Limiter", "Automatic Level Control"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item >= 3)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 3, names);
}
static int wm8776_level_control_get(struct snd_kcontrol *ctl,
@ -851,13 +944,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
"None", "High-pass Filter"
};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item >= 2)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
@ -985,6 +1072,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
.private_value = 0,
},
};
static const struct snd_kcontrol_new hdav_slim_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "HDMI Playback Switch",
.info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put,
.private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Volume",
.info = wm8776_hp_vol_info,
.get = wm8776_hp_vol_get,
.put = wm8776_hp_vol_put,
.tlv = { .p = wm8776_hp_db_scale },
},
WM8776_BIT_SWITCH("Headphone Playback Switch",
WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Capture Volume",
.info = wm8776_input_vol_info,
.get = wm8776_input_vol_get,
.put = wm8776_input_vol_put,
.tlv = { .p = wm8776_adc_db_scale },
},
WM8776_BIT_SWITCH("Mic Capture Switch",
WM8776_ADCMUX, 1 << 0, 0, 0),
WM8776_BIT_SWITCH("Aux Capture Switch",
WM8776_ADCMUX, 1 << 1, 0, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Filter Capture Enum",
.info = hpf_info,
.get = hpf_get,
.put = hpf_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Level Control Capture Enum",
.info = wm8776_level_control_info,
.get = wm8776_level_control_get,
.put = wm8776_level_control_put,
.private_value = 0,
},
};
static const struct snd_kcontrol_new lc_controls[] = {
WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
@ -1028,6 +1162,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
LC_CONTROL_ALC, wm8776_ngth_db_scale),
};
static int add_lc_controls(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
unsigned int i;
struct snd_kcontrol *ctl;
int err;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
ctl = snd_ctl_new1(&lc_controls[i], chip);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
data->lc_controls[i] = ctl;
}
return 0;
}
static int xonar_ds_mixer_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@ -1049,17 +1203,54 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
}
if (!data->line_adcmux_control || !data->mic_adcmux_control)
return -ENXIO;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
ctl = snd_ctl_new1(&lc_controls[i], chip);
return add_lc_controls(chip);
}
static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
{
unsigned int i;
struct snd_kcontrol *ctl;
int err;
for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
if (!ctl)
return -ENOMEM;
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
data->lc_controls[i] = ctl;
}
return 0;
return add_lc_controls(chip);
}
static void dump_wm8776_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct xonar_wm87x6 *data = chip->model_data;
unsigned int i;
snd_iprintf(buffer, "\nWM8776:\n00:");
for (i = 0; i < 0x10; ++i)
snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
snd_iprintf(buffer, "\n10:");
for (i = 0x10; i < 0x17; ++i)
snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
snd_iprintf(buffer, "\n");
}
static void dump_wm87x6_registers(struct oxygen *chip,
struct snd_info_buffer *buffer)
{
struct xonar_wm87x6 *data = chip->model_data;
unsigned int i;
dump_wm8776_registers(chip, buffer);
snd_iprintf(buffer, "\nWM8766:\n00:");
for (i = 0; i < 0x10; ++i)
snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
snd_iprintf(buffer, "\n");
}
static const struct oxygen_model model_xonar_ds = {
@ -1072,22 +1263,57 @@ static const struct oxygen_model model_xonar_ds = {
.suspend = xonar_ds_suspend,
.resume = xonar_ds_resume,
.pcm_hardware_filter = wm8776_adc_hardware_filter,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_wm87x6_dac_params,
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm87x6_volume,
.update_dac_mute = update_wm87x6_mute,
.update_center_lfe_mix = update_wm8766_center_lfe_mix,
.gpio_changed = xonar_ds_gpio_changed,
.dump_registers = dump_wm87x6_registers,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1,
.dac_channels = 8,
.dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI,
.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
static const struct oxygen_model model_xonar_hdav_slim = {
.shortname = "Xonar HDAV1.3 Slim",
.longname = "Asus Virtuoso 200",
.chip = "AV200",
.init = xonar_hdav_slim_init,
.mixer_init = xonar_hdav_slim_mixer_init,
.cleanup = xonar_hdav_slim_cleanup,
.suspend = xonar_hdav_slim_suspend,
.resume = xonar_hdav_slim_resume,
.pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
.set_dac_params = set_hdav_slim_dac_params,
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm8776_volume,
.update_dac_mute = update_wm8776_mute,
.uart_input = xonar_hdmi_uart_input,
.dump_registers = dump_wm8776_registers,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1,
.dac_channels_pcm = 8,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_2WIRE,
.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
};
@ -1099,6 +1325,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
case 0x838e:
chip->model = model_xonar_ds;
break;
case 0x835e:
chip->model = model_xonar_hdav_slim;
break;
default:
return -EINVAL;
}

View File

@ -60,6 +60,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
"{RME HDSP-9652},"
"{RME HDSP-9632}}");
#ifdef HDSP_FW_LOADER
MODULE_FIRMWARE("rpm_firmware.bin");
MODULE_FIRMWARE("multiface_firmware.bin");
MODULE_FIRMWARE("multiface_firmware_rev11.bin");
MODULE_FIRMWARE("digiface_firmware.bin");
@ -81,6 +82,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define H9632_SS_CHANNELS 12
#define H9632_DS_CHANNELS 8
#define H9632_QS_CHANNELS 4
#define RPM_CHANNELS 6
/* Write registers. These are defined as byte-offsets from the iobase value.
*/
@ -191,6 +193,25 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_PhoneGain1 (1<<30)
#define HDSP_QuadSpeed (1<<31)
/* RPM uses some of the registers for special purposes */
#define HDSP_RPM_Inp12 0x04A00
#define HDSP_RPM_Inp12_Phon_6dB 0x00800 /* Dolby */
#define HDSP_RPM_Inp12_Phon_0dB 0x00000 /* .. */
#define HDSP_RPM_Inp12_Phon_n6dB 0x04000 /* inp_0 */
#define HDSP_RPM_Inp12_Line_0dB 0x04200 /* Dolby+PRO */
#define HDSP_RPM_Inp12_Line_n6dB 0x00200 /* PRO */
#define HDSP_RPM_Inp34 0x32000
#define HDSP_RPM_Inp34_Phon_6dB 0x20000 /* SyncRef1 */
#define HDSP_RPM_Inp34_Phon_0dB 0x00000 /* .. */
#define HDSP_RPM_Inp34_Phon_n6dB 0x02000 /* SyncRef2 */
#define HDSP_RPM_Inp34_Line_0dB 0x30000 /* SyncRef1+SyncRef0 */
#define HDSP_RPM_Inp34_Line_n6dB 0x10000 /* SyncRef0 */
#define HDSP_RPM_Bypass 0x01000
#define HDSP_RPM_Disconnect 0x00001
#define HDSP_ADGainMask (HDSP_ADGain0|HDSP_ADGain1)
#define HDSP_ADGainMinus10dBV HDSP_ADGainMask
#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
@ -450,7 +471,7 @@ struct hdsp {
u32 creg_spdif;
u32 creg_spdif_stream;
int clock_source_locked;
char *card_name; /* digiface/multiface */
char *card_name; /* digiface/multiface/rpm */
enum HDSP_IO_Type io_type; /* ditto, but for code use */
unsigned short firmware_rev;
unsigned short state; /* stores state bits */
@ -612,6 +633,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
switch (hdsp->io_type) {
case Multiface:
case Digiface:
case RPM:
default:
if (hdsp->firmware_rev == 0xa)
return (64 * out) + (32 + (in));
@ -629,6 +651,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
switch (hdsp->io_type) {
case Multiface:
case Digiface:
case RPM:
default:
if (hdsp->firmware_rev == 0xa)
return (64 * out) + in;
@ -655,7 +678,7 @@ static int hdsp_check_for_iobox (struct hdsp *hdsp)
{
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
snd_printk("Hammerfall-DSP: no IO box connected!\n");
hdsp->state &= ~HDSP_FirmwareLoaded;
return -EIO;
}
@ -680,7 +703,7 @@ static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
}
}
snd_printk("Hammerfall-DSP: no Digiface or Multiface connected!\n");
snd_printk("Hammerfall-DSP: no IO box connected!\n");
hdsp->state &= ~HDSP_FirmwareLoaded;
return -EIO;
}
@ -752,17 +775,21 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
hdsp_write (hdsp, HDSP_fifoData, 0);
if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) {
hdsp->io_type = Multiface;
hdsp_write (hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) {
hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT))
hdsp->io_type = RPM;
else
hdsp->io_type = Multiface;
} else {
hdsp->io_type = Digiface;
}
} else {
/* firmware was already loaded, get iobox type */
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
hdsp->io_type = RPM;
else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
hdsp->io_type = Multiface;
else
hdsp->io_type = Digiface;
@ -1184,6 +1211,7 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
hdsp->channel_map = channel_map_ds;
} else {
switch (hdsp->io_type) {
case RPM:
case Multiface:
hdsp->channel_map = channel_map_mf_ss;
break;
@ -3231,6 +3259,318 @@ HDSP_PRECISE_POINTER("Precise Pointer", 0),
HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
};
static int hdsp_rpm_input12(struct hdsp *hdsp)
{
switch (hdsp->control_register & HDSP_RPM_Inp12) {
case HDSP_RPM_Inp12_Phon_6dB:
return 0;
case HDSP_RPM_Inp12_Phon_n6dB:
return 2;
case HDSP_RPM_Inp12_Line_0dB:
return 3;
case HDSP_RPM_Inp12_Line_n6dB:
return 4;
}
return 1;
}
static int snd_hdsp_get_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = hdsp_rpm_input12(hdsp);
return 0;
}
static int hdsp_set_rpm_input12(struct hdsp *hdsp, int mode)
{
hdsp->control_register &= ~HDSP_RPM_Inp12;
switch (mode) {
case 0:
hdsp->control_register |= HDSP_RPM_Inp12_Phon_6dB;
break;
case 1:
break;
case 2:
hdsp->control_register |= HDSP_RPM_Inp12_Phon_n6dB;
break;
case 3:
hdsp->control_register |= HDSP_RPM_Inp12_Line_0dB;
break;
case 4:
hdsp->control_register |= HDSP_RPM_Inp12_Line_n6dB;
break;
default:
return -1;
}
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
if (val < 0)
val = 0;
if (val > 4)
val = 4;
spin_lock_irq(&hdsp->lock);
if (val != hdsp_rpm_input12(hdsp))
change = (hdsp_set_rpm_input12(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
spin_unlock_irq(&hdsp->lock);
return change;
}
static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"};
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 int hdsp_rpm_input34(struct hdsp *hdsp)
{
switch (hdsp->control_register & HDSP_RPM_Inp34) {
case HDSP_RPM_Inp34_Phon_6dB:
return 0;
case HDSP_RPM_Inp34_Phon_n6dB:
return 2;
case HDSP_RPM_Inp34_Line_0dB:
return 3;
case HDSP_RPM_Inp34_Line_n6dB:
return 4;
}
return 1;
}
static int snd_hdsp_get_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = hdsp_rpm_input34(hdsp);
return 0;
}
static int hdsp_set_rpm_input34(struct hdsp *hdsp, int mode)
{
hdsp->control_register &= ~HDSP_RPM_Inp34;
switch (mode) {
case 0:
hdsp->control_register |= HDSP_RPM_Inp34_Phon_6dB;
break;
case 1:
break;
case 2:
hdsp->control_register |= HDSP_RPM_Inp34_Phon_n6dB;
break;
case 3:
hdsp->control_register |= HDSP_RPM_Inp34_Line_0dB;
break;
case 4:
hdsp->control_register |= HDSP_RPM_Inp34_Line_n6dB;
break;
default:
return -1;
}
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
static int snd_hdsp_put_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
int val;
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.enumerated.item[0];
if (val < 0)
val = 0;
if (val > 4)
val = 4;
spin_lock_irq(&hdsp->lock);
if (val != hdsp_rpm_input34(hdsp))
change = (hdsp_set_rpm_input34(hdsp, val) == 0) ? 1 : 0;
else
change = 0;
spin_unlock_irq(&hdsp->lock);
return change;
}
/* RPM Bypass switch */
static int hdsp_rpm_bypass(struct hdsp *hdsp)
{
return (hdsp->control_register & HDSP_RPM_Bypass) ? 1 : 0;
}
static int snd_hdsp_get_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = hdsp_rpm_bypass(hdsp);
return 0;
}
static int hdsp_set_rpm_bypass(struct hdsp *hdsp, int on)
{
if (on)
hdsp->control_register |= HDSP_RPM_Bypass;
else
hdsp->control_register &= ~HDSP_RPM_Bypass;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
change = (int)val != hdsp_rpm_bypass(hdsp);
hdsp_set_rpm_bypass(hdsp, val);
spin_unlock_irq(&hdsp->lock);
return change;
}
static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"On", "Off"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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;
}
/* RPM Disconnect switch */
static int hdsp_rpm_disconnect(struct hdsp *hdsp)
{
return (hdsp->control_register & HDSP_RPM_Disconnect) ? 1 : 0;
}
static int snd_hdsp_get_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = hdsp_rpm_disconnect(hdsp);
return 0;
}
static int hdsp_set_rpm_disconnect(struct hdsp *hdsp, int on)
{
if (on)
hdsp->control_register |= HDSP_RPM_Disconnect;
else
hdsp->control_register &= ~HDSP_RPM_Disconnect;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
int change;
unsigned int val;
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
change = (int)val != hdsp_rpm_disconnect(hdsp);
hdsp_set_rpm_disconnect(hdsp, val);
spin_unlock_irq(&hdsp->lock);
return change;
}
static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = {"On", "Off"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 2;
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 struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "RPM Bypass",
.get = snd_hdsp_get_rpm_bypass,
.put = snd_hdsp_put_rpm_bypass,
.info = snd_hdsp_info_rpm_bypass
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "RPM Disconnect",
.get = snd_hdsp_get_rpm_disconnect,
.put = snd_hdsp_put_rpm_disconnect,
.info = snd_hdsp_info_rpm_disconnect
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input 1/2",
.get = snd_hdsp_get_rpm_input12,
.put = snd_hdsp_put_rpm_input12,
.info = snd_hdsp_info_rpm_input
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input 3/4",
.get = snd_hdsp_get_rpm_input34,
.put = snd_hdsp_put_rpm_input34,
.info = snd_hdsp_info_rpm_input
},
HDSP_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
HDSP_MIXER("Mixer", 0)
};
static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
@ -3240,6 +3580,16 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
int err;
struct snd_kcontrol *kctl;
if (hdsp->io_type == RPM) {
/* RPM Bypass, Disconnect and Input switches */
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_rpm_controls); idx++) {
err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
if (err < 0)
return err;
}
return 0;
}
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
return err;
@ -3459,48 +3809,102 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "\n");
switch (hdsp_spdif_in(hdsp)) {
case HDSP_SPDIFIN_OPTICAL:
snd_iprintf(buffer, "IEC958 input: Optical\n");
break;
case HDSP_SPDIFIN_COAXIAL:
snd_iprintf(buffer, "IEC958 input: Coaxial\n");
break;
case HDSP_SPDIFIN_INTERNAL:
snd_iprintf(buffer, "IEC958 input: Internal\n");
break;
case HDSP_SPDIFIN_AES:
snd_iprintf(buffer, "IEC958 input: AES\n");
break;
default:
snd_iprintf(buffer, "IEC958 input: ???\n");
break;
if (hdsp->io_type != RPM) {
switch (hdsp_spdif_in(hdsp)) {
case HDSP_SPDIFIN_OPTICAL:
snd_iprintf(buffer, "IEC958 input: Optical\n");
break;
case HDSP_SPDIFIN_COAXIAL:
snd_iprintf(buffer, "IEC958 input: Coaxial\n");
break;
case HDSP_SPDIFIN_INTERNAL:
snd_iprintf(buffer, "IEC958 input: Internal\n");
break;
case HDSP_SPDIFIN_AES:
snd_iprintf(buffer, "IEC958 input: AES\n");
break;
default:
snd_iprintf(buffer, "IEC958 input: ???\n");
break;
}
}
if (hdsp->control_register & HDSP_SPDIFOpticalOut)
snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
else
snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
if (RPM == hdsp->io_type) {
if (hdsp->control_register & HDSP_RPM_Bypass)
snd_iprintf(buffer, "RPM Bypass: disabled\n");
else
snd_iprintf(buffer, "RPM Bypass: enabled\n");
if (hdsp->control_register & HDSP_RPM_Disconnect)
snd_iprintf(buffer, "RPM disconnected\n");
else
snd_iprintf(buffer, "RPM connected\n");
if (hdsp->control_register & HDSP_SPDIFProfessional)
snd_iprintf(buffer, "IEC958 quality: Professional\n");
else
snd_iprintf(buffer, "IEC958 quality: Consumer\n");
switch (hdsp->control_register & HDSP_RPM_Inp12) {
case HDSP_RPM_Inp12_Phon_6dB:
snd_iprintf(buffer, "Input 1/2: Phono, 6dB\n");
break;
case HDSP_RPM_Inp12_Phon_0dB:
snd_iprintf(buffer, "Input 1/2: Phono, 0dB\n");
break;
case HDSP_RPM_Inp12_Phon_n6dB:
snd_iprintf(buffer, "Input 1/2: Phono, -6dB\n");
break;
case HDSP_RPM_Inp12_Line_0dB:
snd_iprintf(buffer, "Input 1/2: Line, 0dB\n");
break;
case HDSP_RPM_Inp12_Line_n6dB:
snd_iprintf(buffer, "Input 1/2: Line, -6dB\n");
break;
default:
snd_iprintf(buffer, "Input 1/2: ???\n");
}
if (hdsp->control_register & HDSP_SPDIFEmphasis)
snd_iprintf(buffer, "IEC958 emphasis: on\n");
else
snd_iprintf(buffer, "IEC958 emphasis: off\n");
switch (hdsp->control_register & HDSP_RPM_Inp34) {
case HDSP_RPM_Inp34_Phon_6dB:
snd_iprintf(buffer, "Input 3/4: Phono, 6dB\n");
break;
case HDSP_RPM_Inp34_Phon_0dB:
snd_iprintf(buffer, "Input 3/4: Phono, 0dB\n");
break;
case HDSP_RPM_Inp34_Phon_n6dB:
snd_iprintf(buffer, "Input 3/4: Phono, -6dB\n");
break;
case HDSP_RPM_Inp34_Line_0dB:
snd_iprintf(buffer, "Input 3/4: Line, 0dB\n");
break;
case HDSP_RPM_Inp34_Line_n6dB:
snd_iprintf(buffer, "Input 3/4: Line, -6dB\n");
break;
default:
snd_iprintf(buffer, "Input 3/4: ???\n");
}
if (hdsp->control_register & HDSP_SPDIFNonAudio)
snd_iprintf(buffer, "IEC958 NonAudio: on\n");
else
snd_iprintf(buffer, "IEC958 NonAudio: off\n");
if ((x = hdsp_spdif_sample_rate (hdsp)) != 0)
snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
else
snd_iprintf (buffer, "IEC958 sample rate: Error flag set\n");
} else {
if (hdsp->control_register & HDSP_SPDIFOpticalOut)
snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
else
snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
if (hdsp->control_register & HDSP_SPDIFProfessional)
snd_iprintf(buffer, "IEC958 quality: Professional\n");
else
snd_iprintf(buffer, "IEC958 quality: Consumer\n");
if (hdsp->control_register & HDSP_SPDIFEmphasis)
snd_iprintf(buffer, "IEC958 emphasis: on\n");
else
snd_iprintf(buffer, "IEC958 emphasis: off\n");
if (hdsp->control_register & HDSP_SPDIFNonAudio)
snd_iprintf(buffer, "IEC958 NonAudio: on\n");
else
snd_iprintf(buffer, "IEC958 NonAudio: off\n");
x = hdsp_spdif_sample_rate(hdsp);
if (x != 0)
snd_iprintf(buffer, "IEC958 sample rate: %d\n", x);
else
snd_iprintf(buffer, "IEC958 sample rate: Error flag set\n");
}
snd_iprintf(buffer, "\n");
/* Sync Check */
@ -3765,7 +4169,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
snd_hdsp_midi_input_read (&hdsp->midi[0]);
}
}
if (hdsp->io_type != Multiface && hdsp->io_type != H9632 && midi1 && midi1status) {
if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) {
if (hdsp->use_midi_tasklet) {
/* we disable interrupts for this input until processing is done */
hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
@ -4093,7 +4497,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
.channels_min = 14,
.channels_min = 6,
.channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10,
@ -4122,7 +4526,7 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
SNDRV_PCM_RATE_96000),
.rate_min = 32000,
.rate_max = 96000,
.channels_min = 14,
.channels_min = 5,
.channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10,
@ -4357,10 +4761,12 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
snd_hdsp_hw_rule_rate_out_channels, hdsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
hdsp->creg_spdif_stream = hdsp->creg_spdif;
hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
if (RPM != hdsp->io_type) {
hdsp->creg_spdif_stream = hdsp->creg_spdif;
hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
}
return 0;
}
@ -4375,9 +4781,11 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
spin_unlock_irq(&hdsp->lock);
hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
if (RPM != hdsp->io_type) {
hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
}
return 0;
}
@ -4616,7 +5024,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
if (hdsp->io_type != H9632)
info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != H9632) ? 3 : 1); ++i)
for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i)
info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
@ -4636,6 +5044,9 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
} else if (hdsp->io_type == RPM) {
info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp);
info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp);
}
if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
@ -4844,6 +5255,14 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
break;
case RPM:
hdsp->card_name = "RME Hammerfall DSP + RPM";
hdsp->ss_in_channels = RPM_CHANNELS-1;
hdsp->ss_out_channels = RPM_CHANNELS;
hdsp->ds_in_channels = RPM_CHANNELS-1;
hdsp->ds_out_channels = RPM_CHANNELS;
break;
default:
/* should never get here */
break;
@ -4930,6 +5349,9 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
/* caution: max length of firmware filename is 30! */
switch (hdsp->io_type) {
case RPM:
fwfile = "rpm_firmware.bin";
break;
case Multiface:
if (hdsp->firmware_rev == 0xa)
fwfile = "multiface_firmware.bin";
@ -5100,7 +5522,9 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
return 0;
} else {
snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
hdsp->io_type = RPM;
else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
hdsp->io_type = Multiface;
else
hdsp->io_type = Digiface;

View File

@ -1389,15 +1389,9 @@ static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
{
static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
static const char *const texts[3] = {"AC'97", "IEC958", "ZV Port"};
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item > 2)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 3, texts);
}
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)

View File

@ -1626,7 +1626,6 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec)
{
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
int ret;
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
@ -1641,15 +1640,9 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec)
priv->hpr.jack = NULL;
priv->mic.jack = NULL;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(&codec->delayed_work);
/* if there was any work waiting then we run it now and
* wait for its completion */
if (ret) {
schedule_delayed_work(&codec->delayed_work, 0);
flush_scheduled_work();
}
flush_delayed_work_sync(&codec->delayed_work);
wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -1476,25 +1476,6 @@ static int wm8753_resume(struct snd_soc_codec *codec)
return 0;
}
/*
* This function forces any delayed work to be queued and run.
*/
static int run_delayed_work(struct delayed_work *dwork)
{
int ret;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(dwork);
/* if there was any work waiting then we run it now and
* wait for it's completion */
if (ret) {
schedule_delayed_work(dwork, 0);
flush_scheduled_work();
}
return ret;
}
static int wm8753_probe(struct snd_soc_codec *codec)
{
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
@ -1544,7 +1525,7 @@ static int wm8753_probe(struct snd_soc_codec *codec)
/* power down chip */
static int wm8753_remove(struct snd_soc_codec *codec)
{
run_delayed_work(&codec->delayed_work);
flush_delayed_work_sync(&codec->delayed_work);
wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;

View File

@ -67,25 +67,6 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
/*
* This function forces any delayed work to be queued and run.
*/
static int run_delayed_work(struct delayed_work *dwork)
{
int ret;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(dwork);
/* if there was any work waiting then we run it now and
* wait for it's completion */
if (ret) {
schedule_delayed_work(dwork, 0);
flush_scheduled_work();
}
return ret;
}
/* codec register dump */
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
{
@ -1016,7 +997,7 @@ static int soc_suspend(struct device *dev)
/* close any waiting streams and save state */
for (i = 0; i < card->num_rtd; i++) {
run_delayed_work(&card->rtd[i].delayed_work);
flush_delayed_work_sync(&card->rtd[i].delayed_work);
card->rtd[i].codec->suspend_bias_level = card->rtd[i].codec->bias_level;
}
@ -1689,7 +1670,7 @@ static int soc_remove(struct platform_device *pdev)
/* make sure any delayed work runs */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
run_delayed_work(&rtd->delayed_work);
flush_delayed_work_sync(&rtd->delayed_work);
}
/* remove and free each DAI */
@ -1720,7 +1701,7 @@ static int soc_poweroff(struct device *dev)
* now, we're shutting down so no imminent restart. */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
run_delayed_work(&rtd->delayed_work);
flush_delayed_work_sync(&rtd->delayed_work);
}
snd_soc_dapm_shutdown(card);

View File

@ -76,7 +76,10 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
format = 1 << UAC_FORMAT_TYPE_I_PCM;
}
if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
if (sample_width > sample_bytes * 8) {
if (chip->usb_id == USB_ID(0x0582, 0x0016) /* Edirol SD-90 */ &&
sample_width == 24 && sample_bytes == 2)
sample_bytes = 3;
else if (sample_width > sample_bytes * 8) {
snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
chip->dev->devnum, fp->iface, fp->altsetting,
sample_width, sample_bytes);

View File

@ -850,8 +850,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
return;
}
memset(urb->transfer_buffer + count, 0xFD, 9 - count);
urb->transfer_buffer_length = count;
memset(urb->transfer_buffer + count, 0xFD, ep->max_transfer - count);
urb->transfer_buffer_length = ep->max_transfer;
}
static struct usb_protocol_ops snd_usbmidi_122l_ops = {
@ -1295,6 +1295,13 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */
ep->max_transfer = 4;
break;
/*
* Some devices only work with 9 bytes packet size:
*/
case USB_ID(0x0644, 0x800E): /* Tascam US-122L */
case USB_ID(0x0644, 0x800F): /* Tascam US-144 */
ep->max_transfer = 9;
break;
}
for (i = 0; i < OUTPUT_URBS; ++i) {
buffer = usb_alloc_coherent(umidi->dev,
@ -1729,13 +1736,7 @@ static int roland_load_info(struct snd_kcontrol *kcontrol,
{
static const char *const names[] = { "High Load", "Light Load" };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1;
info->value.enumerated.items = 2;
if (info->value.enumerated.item > 1)
info->value.enumerated.item = 1;
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
return 0;
return snd_ctl_enum_info(info, 1, 2, names);
}
static int roland_load_get(struct snd_kcontrol *kcontrol,

View File

@ -1633,18 +1633,11 @@ static int parse_audio_extension_unit(struct mixer_build *state, int unitid, voi
static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct usb_mixer_elem_info *cval = kcontrol->private_data;
char **itemlist = (char **)kcontrol->private_value;
const char **itemlist = (const char **)kcontrol->private_value;
if (snd_BUG_ON(!itemlist))
return -EINVAL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = cval->max;
if (uinfo->value.enumerated.item >= cval->max)
uinfo->value.enumerated.item = cval->max - 1;
strlcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item],
sizeof(uinfo->value.enumerated.name));
return 0;
return snd_ctl_enum_info(uinfo, 1, cval->max, itemlist);
}
/* get callback for selector unit */

View File

@ -705,11 +705,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
.type = QUIRK_IGNORE_INTERFACE
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 1,
.type = QUIRK_IGNORE_INTERFACE
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,

View File

@ -273,29 +273,26 @@ static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
struct file *file, poll_table *wait)
{
struct us122l *us122l = hw->private_data;
struct usb_stream *s = us122l->sk.s;
unsigned *polled;
unsigned int mask;
poll_wait(file, &us122l->sk.sleep, wait);
switch (s->state) {
case usb_stream_ready:
if (us122l->first == file)
polled = &s->periods_polled;
else
polled = &us122l->second_periods_polled;
if (*polled != s->periods_done) {
*polled = s->periods_done;
mask = POLLIN | POLLOUT | POLLWRNORM;
break;
mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
if (mutex_trylock(&us122l->mutex)) {
struct usb_stream *s = us122l->sk.s;
if (s && s->state == usb_stream_ready) {
if (us122l->first == file)
polled = &s->periods_polled;
else
polled = &us122l->second_periods_polled;
if (*polled != s->periods_done) {
*polled = s->periods_done;
mask = POLLIN | POLLOUT | POLLWRNORM;
} else
mask = 0;
}
/* Fall through */
mask = 0;
break;
default:
mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
break;
mutex_unlock(&us122l->mutex);
}
return mask;
}
@ -381,6 +378,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
{
struct usb_stream_config *cfg;
struct us122l *us122l = hw->private_data;
struct usb_stream *s;
unsigned min_period_frames;
int err = 0;
bool high_speed;
@ -426,18 +424,18 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
mutex_lock(&us122l->mutex);
s = us122l->sk.s;
if (!us122l->master)
us122l->master = file;
else if (us122l->master != file) {
if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) {
if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg))) {
err = -EIO;
goto unlock;
}
us122l->slave = file;
}
if (!us122l->sk.s ||
memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) ||
us122l->sk.s->state == usb_stream_xrun) {
if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg)) ||
s->state == usb_stream_xrun) {
us122l_stop(us122l);
if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
err = -EIO;
@ -448,6 +446,7 @@ unlock:
mutex_unlock(&us122l->mutex);
free:
kfree(cfg);
wake_up_all(&us122l->sk.sleep);
return err;
}