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. 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 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 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 * Asound A-8788
* Asus Xonar DG
* AuzenTech X-Meridian * AuzenTech X-Meridian
* AuzenTech X-Meridian 2G
* Bgears b-Enspirer * Bgears b-Enspirer
* Club3D Theatron DTS * Club3D Theatron DTS
* HT-Omega Claro (plus) * HT-Omega Claro (plus)
* HT-Omega Claro halo (XT) * HT-Omega Claro halo (XT)
* Kuroutoshikou CMI8787-HG2PCI
* Razer Barracuda AC-1 * Razer Barracuda AC-1
* Sondigo Inferno * Sondigo Inferno
* TempoTec HiFier Fantasia
* TempoTec HiFier Serenade
This module supports autoprobe and multiple cards. 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 snd-virtuoso
------------------- -------------------
Module for sound cards based on the Asus AV100/AV200 chips, Module for sound cards based on the Asus AV66/AV100/AV200 chips,
i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
(Deluxe) and Essence STX. HDAV1.3 (Deluxe), and HDAV1.3 Slim.
This module supports autoprobe and multiple cards. This module supports autoprobe and multiple cards.

View File

@ -1434,6 +1434,14 @@ S: Supported
F: block/bsg.c F: block/bsg.c
F: include/linux/bsg.h 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 BT8XXGPIO DRIVER
M: Michael Buesch <mb@bu3sch.de> M: Michael Buesch <mb@bu3sch.de>
W: http://bu3sch.de/btgpio.php W: http://bu3sch.de/btgpio.php
@ -1459,6 +1467,13 @@ S: Maintained
F: Documentation/video4linux/bttv/ F: Documentation/video4linux/bttv/
F: drivers/media/video/bt8xx/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 CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
M: David Howells <dhowells@redhat.com> M: David Howells <dhowells@redhat.com>
L: linux-cachefs@redhat.com L: linux-cachefs@redhat.com
@ -2249,6 +2264,13 @@ W: bluesmoke.sourceforge.net
S: Maintained S: Maintained
F: drivers/edac/r82600_edac.c 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 EEEPC LAPTOP EXTRAS DRIVER
M: Corentin Chary <corentincj@iksaif.net> M: Corentin Chary <corentincj@iksaif.net>
L: acpi4asus-user@lists.sourceforge.net L: acpi4asus-user@lists.sourceforge.net
@ -3393,6 +3415,13 @@ L: linux-serial@vger.kernel.org
S: Maintained S: Maintained
F: drivers/serial/jsm/ 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 K8TEMP HARDWARE MONITORING DRIVER
M: Rudolf Marek <r.marek@assembler.cz> M: Rudolf Marek <r.marek@assembler.cz>
L: lm-sensors@lm-sensors.org L: lm-sensors@lm-sensors.org
@ -4409,6 +4438,13 @@ F: drivers/of
F: include/linux/of*.h F: include/linux/of*.h
K: of_get_property 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 OPROFILE
M: Robert Richter <robert.richter@amd.com> M: Robert Richter <robert.richter@amd.com>
L: oprofile-list@lists.sf.net L: oprofile-list@lists.sf.net
@ -6141,6 +6177,13 @@ S: Maintained
W: http://www.one-eyed-alien.net/~mdharm/linux-usb/ W: http://www.one-eyed-alien.net/~mdharm/linux-usb/
F: drivers/usb/storage/ 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 USB OHCI DRIVER
M: David Brownell <dbrownell@users.sourceforge.net> M: David Brownell <dbrownell@users.sourceforge.net>
L: linux-usb@vger.kernel.org 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_HALF_DUPLEX 0x00100000 /* only half duplex */
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #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_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 */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
typedef int __bitwise snd_pcm_state_t; 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_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_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 { struct snd_interval {
unsigned int min, max; 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, int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); struct snd_ctl_elem_info *uinfo);
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); 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 * virtual master control

View File

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

View File

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

View File

@ -297,6 +297,7 @@ struct snd_pcm_runtime {
unsigned int info; unsigned int info;
unsigned int rate_num; unsigned int rate_num;
unsigned int rate_den; unsigned int rate_den;
unsigned int no_period_wakeup: 1;
/* -- SW params -- */ /* -- SW params -- */
int tstamp_mode; /* mmap timestamp is updated */ 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 * 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 * probe method. Drivers have direct access to the struct snd_ac97
* decide based on the id field amongst other things. * structure and may decide based on the id field amongst other things.
*/ */
static int ac97_bus_match(struct device *dev, struct device_driver *drv) 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); of_node_put(onyx->codec.node);
if (onyx->codec_info) if (onyx->codec_info)
kfree(onyx->codec_info); kfree(onyx->codec_info);
i2c_set_clientdata(client, onyx);
kfree(onyx); kfree(onyx);
return 0; 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); free_irq(linein_detect_irq, &rt->line_in_notify);
if (rt->line_out_notify.gpio_private) if (rt->line_out_notify.gpio_private)
free_irq(lineout_detect_irq, &rt->line_out_notify); free_irq(lineout_detect_irq, &rt->line_out_notify);
cancel_delayed_work(&rt->headphone_notify.work); cancel_delayed_work_sync(&rt->headphone_notify.work);
cancel_delayed_work(&rt->line_in_notify.work); cancel_delayed_work_sync(&rt->line_in_notify.work);
cancel_delayed_work(&rt->line_out_notify.work); cancel_delayed_work_sync(&rt->line_out_notify.work);
flush_scheduled_work();
mutex_destroy(&rt->headphone_notify.mutex); mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex); mutex_destroy(&rt->line_in_notify.mutex);
mutex_destroy(&rt->line_out_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 /* make sure no work is pending before freeing
* all things */ * all things */
cancel_delayed_work(&rt->headphone_notify.work); cancel_delayed_work_sync(&rt->headphone_notify.work);
cancel_delayed_work(&rt->line_in_notify.work); cancel_delayed_work_sync(&rt->line_in_notify.work);
cancel_delayed_work(&rt->line_out_notify.work); cancel_delayed_work_sync(&rt->line_out_notify.work);
flush_scheduled_work();
mutex_destroy(&rt->headphone_notify.mutex); mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_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, int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) 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); 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 { } else {
*params = *save; *params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
if (max < 0) if (max < 0) {
kfree(save);
return max; return max;
}
last = 1; last = 1;
} }
_end: _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)new_hw_ptr,
(unsigned long)runtime->hw_ptr_base); (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 */ /* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) { if (delta >= runtime->buffer_size + runtime->period_size) {
hw_ptr_error(substream, hw_ptr_error(substream,
@ -442,6 +463,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
(long)old_hw_ptr); (long)old_hw_ptr);
} }
no_delta_check:
if (runtime->status->hw_ptr == new_hw_ptr) if (runtime->status->hw_ptr == new_hw_ptr)
return 0; return 0;

View File

@ -422,6 +422,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->info = params->info; runtime->info = params->info;
runtime->rate_num = params->rate_num; runtime->rate_num = params->rate_num;
runtime->rate_den = params->rate_den; 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); bits = snd_pcm_format_physical_width(runtime->format);
runtime->sample_bits = bits; runtime->sample_bits = bits;

View File

@ -32,6 +32,7 @@
#include "seq_timer.h" #include "seq_timer.h"
#include "seq_system.h" #include "seq_system.h"
#include "seq_info.h" #include "seq_info.h"
#include <sound/minors.h>
#include <sound/seq_device.h> #include <sound/seq_device.h>
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE) #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_param(seq_default_timer_resolution, int, 0644);
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz."); 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 * INIT PART
*/ */

View File

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

View File

@ -34,8 +34,8 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE) #if defined(CONFIG_SND_HRTIMER) || defined(CONFIG_SND_HRTIMER_MODULE)
#define DEFAULT_TIMER_LIMIT 3 #define DEFAULT_TIMER_LIMIT 4
#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE) #elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
#define DEFAULT_TIMER_LIMIT 2 #define DEFAULT_TIMER_LIMIT 2
#else #else
@ -52,6 +52,9 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
module_param(timer_tstamp_monotonic, int, 0444); module_param(timer_tstamp_monotonic, int, 0444);
MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); 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_user {
struct snd_timer_instance *timeri; struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */ 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); (resource->start) + 1);
if (ml403_ac97cr->port == NULL) { if (ml403_ac97cr->port == NULL) {
snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
"unable to remap memory region (%x to %x)\n", "unable to remap memory region (%pR)\n",
resource->start, resource->end); resource);
snd_ml403_ac97cr_free(ml403_ac97cr); snd_ml403_ac97cr_free(ml403_ac97cr);
return -EBUSY; return -EBUSY;
} }

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/* /*
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). * 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. * Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801), * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@ -175,6 +175,7 @@
#include <asm/io.h> #include <asm/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/bug.h> /* WARN_ONCE */
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -201,14 +202,15 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
/* === Debug settings === /* === Debug settings ===
Further diagnostic functionality than the settings below 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): 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 local descr=$1; local addr=$2; local count=$3
echo "${descr}: ${count} @ ${addr}:" 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 and then use something like
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8", "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. possibly within a "while true; do ... sleep 1; done" loop.
Tweaking ports could be done using Tweaking ports could be done using
VALSTRING="`printf "%02x" $value`" 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_MISC 0
#define DEBUG_CALLS 0 #define DEBUG_CALLS 0
#define DEBUG_MIXER 0 #define DEBUG_MIXER 0
#define DEBUG_CODEC 0 #define DEBUG_CODEC 0
#define DEBUG_IO 0
#define DEBUG_TIMER 0 #define DEBUG_TIMER 0
#define DEBUG_GAME 0 #define DEBUG_GAME 0
#define DEBUG_PM 0 #define DEBUG_PM 0
@ -291,19 +293,23 @@ static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444); module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128."); 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 { enum snd_azf3328_codec_type {
/* warning: fixed indices (also used for bitmask checks!) */
AZF_CODEC_PLAYBACK = 0, AZF_CODEC_PLAYBACK = 0,
AZF_CODEC_CAPTURE = 1, AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2, 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 { struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */ /* often-used fields towards beginning, then grouped */
@ -362,6 +368,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int static int
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set) 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; u8 prev = inb(reg), new;
new = (do_set) ? (prev|mask) : (prev & ~mask); 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); 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 static inline u32
snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) 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 static void
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
enum snd_azf3328_codec_type codec_type,
enum azf_freq_t bitrate, enum azf_freq_t bitrate,
unsigned int format_width, unsigned int format_width,
unsigned int channels unsigned int channels
) )
{ {
unsigned long flags; unsigned long flags;
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00; u16 val = 0xff00;
u8 freq = 0;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
switch (bitrate) { switch (bitrate) {
case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break; #define AZF_FMT_XLATE(in_freq, out_bits) \
case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break; do { \
case AZF_FREQ_5512: case AZF_FREQ_ ## in_freq: \
/* the AZF3328 names it "5510" for some strange reason */ freq = SOUNDFORMAT_FREQ_ ## out_bits; \
val |= SOUNDFORMAT_FREQ_5510; break; break; \
case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break; } while (0);
case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break; AZF_FMT_XLATE(4000, SUSPECTED_4000)
case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break; AZF_FMT_XLATE(4800, SUSPECTED_4800)
case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break; /* the AZF3328 names it "5510" for some strange reason: */
case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break; AZF_FMT_XLATE(5512, 5510)
case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break; AZF_FMT_XLATE(6620, 6620)
case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break; AZF_FMT_XLATE(8000, 8000)
case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break; 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: default:
snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate); snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
/* fall-through */ /* fall-through */
case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break; AZF_FMT_XLATE(44100, 44100)
case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break; AZF_FMT_XLATE(48000, 48000)
case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break; AZF_FMT_XLATE(66200, SUSPECTED_66200)
#undef AZF_FMT_XLATE
} }
/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */ /* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */ /* 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 = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */ /* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
val |= freq;
if (channels == 2) if (channels == 2)
val |= SOUNDFORMAT_FLAG_2CHANNELS; val |= SOUNDFORMAT_FLAG_2CHANNELS;
if (format_width == 16) if (format_width == 16)
val |= SOUNDFORMAT_FLAG_16BIT; val |= SOUNDFORMAT_FLAG_16BIT;
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(codec->lock, flags);
/* set bitrate/format */ /* set bitrate/format */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val); 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: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex * FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */ * 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_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) | snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING1 |
@ -1014,20 +1046,19 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
DMA_SOMETHING_ELSE DMA_SOMETHING_ELSE
); );
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(codec->lock, flags);
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
} }
static inline void static inline void
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip, snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
enum snd_azf3328_codec_type codec_type
) )
{ {
/* choose lowest frequency for low power consumption. /* choose lowest frequency for low power consumption.
* While this will cause louder noise due to rather coarse frequency, * While this will cause louder noise due to rather coarse frequency,
* it should never matter since output should always * it should never matter since output should always
* get disabled properly when idle anyway. */ * 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 static void
@ -1101,69 +1132,87 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
/* ...and adjust clock, too /* ...and adjust clock, too
* (reduce noise and power consumption) */ * (reduce noise and power consumption) */
if (!enable) if (!enable)
snd_azf3328_codec_setfmt_lowpower( snd_azf3328_codec_setfmt_lowpower(codec);
chip,
codec_type
);
codec->running = enable; codec->running = enable;
} }
} }
static void static void
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip, snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
enum snd_azf3328_codec_type codec_type,
unsigned long addr, unsigned long addr,
unsigned int count, unsigned int period_bytes,
unsigned int size unsigned int buffer_bytes
) )
{ {
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter(); 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) { if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */ /* AZF3328 uses a two buffer pointer DMA transfer approach */
unsigned long flags, addr_area2; unsigned long flags;
/* width 32bit (prevent overflow): */ /* 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; area_length = buffer_bytes/2;
addr_area2 = addr+count_areas;
snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
addr, count_areas, addr_area2, count_areas);
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 */ /* build combined I/O buffer length word */
lengths = (count_areas << 16) | (count_areas); setup_io.dma_lengths = (area_length << 16) | (area_length);
spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr); spin_lock_irqsave(codec->lock, flags);
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2, snd_azf3328_codec_outl_multi(
addr_area2); codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS, );
lengths); spin_unlock_irqrestore(codec->lock, flags);
spin_unlock_irqrestore(&chip->reg_lock, flags);
} }
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
} }
static int 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_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 size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream); unsigned int count = snd_pcm_lib_period_bytes(substream);
#endif #endif
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
codec->dma_base = runtime->dma_addr;
#if 0 #if 0
snd_azf3328_codec_setfmt(chip, AZF_CODEC_..., snd_azf3328_codec_setfmt(codec,
runtime->rate, runtime->rate,
snd_pcm_format_width(runtime->format), snd_pcm_format_width(runtime->format),
runtime->channels); runtime->channels);
snd_azf3328_codec_setdmaa(chip, AZF_CODEC_..., snd_azf3328_codec_setdmaa(codec,
runtime->dma_addr, count, size); runtime->dma_addr, count, size);
#endif #endif
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
@ -1171,24 +1220,23 @@ snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
} }
static int static int
snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type, snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); 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_pcm_runtime *runtime = substream->runtime;
struct snd_azf3328_codec_data *codec = runtime->private_data;
int result = 0; int result = 0;
u16 flags1; u16 flags1;
bool previously_muted = 0; 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) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
snd_azf3328_dbgcodec("START %s\n", codec->name); snd_azf3328_dbgcodec("START %s\n", codec->name);
if (is_playback_codec) { if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */ /* mute WaveOut (avoid clicking during setup) */
previously_muted = previously_muted =
snd_azf3328_mixer_set_mute( 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, runtime->rate,
snd_pcm_format_width(runtime->format), snd_pcm_format_width(runtime->format),
runtime->channels); runtime->channels);
spin_lock(&chip->reg_lock); spin_lock(codec->lock);
/* first, remember current value: */ /* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); 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??? */ /* FIXME: clear interrupts or what??? */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff); 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_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream) snd_pcm_lib_buffer_bytes(substream)
); );
spin_lock(&chip->reg_lock); spin_lock(codec->lock);
#ifdef WIN9X #ifdef WIN9X
/* FIXME: enable playback/recording??? */ /* FIXME: enable playback/recording??? */
flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2; 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_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE); DMA_SOMETHING_ELSE);
#endif #endif
spin_unlock(&chip->reg_lock); spin_unlock(codec->lock);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 1); snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
if (is_playback_codec) { if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */ /* now unmute WaveOut */
if (!previously_muted) if (!previously_muted)
snd_azf3328_mixer_set_mute( 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: case SNDRV_PCM_TRIGGER_RESUME:
snd_azf3328_dbgcodec("RESUME %s\n", codec->name); snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
/* resume codec if we were active */ /* resume codec if we were active */
spin_lock(&chip->reg_lock); spin_lock(codec->lock);
if (codec->running) if (codec->running)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw( snd_azf3328_codec_inw(
codec, IDX_IO_CODEC_DMA_FLAGS codec, IDX_IO_CODEC_DMA_FLAGS
) | DMA_RESUME ) | DMA_RESUME
); );
spin_unlock(&chip->reg_lock); spin_unlock(codec->lock);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
snd_azf3328_dbgcodec("STOP %s\n", codec->name); snd_azf3328_dbgcodec("STOP %s\n", codec->name);
if (is_playback_codec) { if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */ /* mute WaveOut (avoid clicking during setup) */
previously_muted = previously_muted =
snd_azf3328_mixer_set_mute( 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: */ /* first, remember current value: */
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS); 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; flags1 &= ~DMA_RUN_SOMETHING1;
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1); snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
spin_unlock(&chip->reg_lock); spin_unlock(codec->lock);
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
if (is_playback_codec) { if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */ /* now unmute WaveOut */
if (!previously_muted) if (!previously_muted)
snd_azf3328_mixer_set_mute( snd_azf3328_mixer_set_mute(
@ -1330,67 +1378,29 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
return result; 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 static snd_pcm_uframes_t
snd_azf3328_codec_pointer(struct snd_pcm_substream *substream, snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
enum snd_azf3328_codec_type codec_type
) )
{ {
const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); const struct snd_azf3328_codec_data *codec =
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type]; substream->runtime->private_data;
unsigned long bufptr, result; unsigned long result;
snd_pcm_uframes_t frmres; 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); result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */ /* 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); frmres = bytes_to_frames( substream->runtime, result);
snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n", snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
codec->name, result, frmres); jiffies, codec->name, result, frmres);
return 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 #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 */ * enter this function */
/* for some very, very strange reason we cannot enable /* for some very, very strange reason we cannot enable
@ -1624,29 +1634,29 @@ snd_azf3328_irq_log_unknown_type(u8 which)
} }
static inline void 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; u8 which;
enum snd_azf3328_codec_type codec_type; 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; for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT; codec_type <= AZF_CODEC_I2S_OUT;
++codec_type) { ++codec_type, ++codec) {
/* skip codec if there's no interrupt for it */ /* skip codec if there's no interrupt for it */
if (!(status & (1 << codec_type))) if (!(status & (1 << codec_type)))
continue; continue;
codec = &chip->codecs[codec_type]; spin_lock(codec->lock);
spin_lock(&chip->reg_lock);
which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE); which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
/* ack all IRQ types immediately */ /* ack all IRQ types immediately */
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which); 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_pcm_period_elapsed(codec->substream);
snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n", snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
codec->name, codec->name,
@ -1701,7 +1711,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
} }
if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT)) 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) if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip); 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_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
chip->codecs[codec_type].substream = substream; codec->substream = substream;
/* same parameters for all our codecs - at least we think so... */ /* same parameters for all our codecs - at least we think so... */
runtime->hw = snd_azf3328_hardware; runtime->hw = snd_azf3328_hardware;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates); &snd_azf3328_hw_constraints_rates);
runtime->private_data = codec;
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; return 0;
} }
static int 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); return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
} }
static int 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); return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
} }
static int 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); return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
} }
static int static int
snd_azf3328_pcm_close(struct snd_pcm_substream *substream, snd_azf3328_pcm_close(struct snd_pcm_substream *substream
enum snd_azf3328_codec_type codec_type
) )
{ {
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_azf3328_codec_data *codec =
substream->runtime->private_data;
snd_azf3328_dbgcallenter(); snd_azf3328_dbgcallenter();
chip->codecs[codec_type].substream = NULL; codec->substream = NULL;
snd_azf3328_dbgcallleave(); snd_azf3328_dbgcallleave();
return 0; 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 = { static struct snd_pcm_ops snd_azf3328_playback_ops = {
.open = snd_azf3328_playback_open, .open = snd_azf3328_pcm_playback_open,
.close = snd_azf3328_playback_close, .close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params, .hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free, .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare, .prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_codec_playback_trigger, .trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_codec_playback_pointer .pointer = snd_azf3328_pcm_pointer
}; };
static struct snd_pcm_ops snd_azf3328_capture_ops = { static struct snd_pcm_ops snd_azf3328_capture_ops = {
.open = snd_azf3328_capture_open, .open = snd_azf3328_pcm_capture_open,
.close = snd_azf3328_capture_close, .close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params, .hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free, .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare, .prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_codec_capture_trigger, .trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_codec_capture_pointer .pointer = snd_azf3328_pcm_pointer
}; };
static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
.open = snd_azf3328_i2s_out_open, .open = snd_azf3328_pcm_i2s_out_open,
.close = snd_azf3328_i2s_out_close, .close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_azf3328_hw_params, .hw_params = snd_azf3328_hw_params,
.hw_free = snd_azf3328_hw_free, .hw_free = snd_azf3328_hw_free,
.prepare = snd_azf3328_codec_prepare, .prepare = snd_azf3328_pcm_prepare,
.trigger = snd_azf3328_codec_i2s_out_trigger, .trigger = snd_azf3328_pcm_trigger,
.pointer = snd_azf3328_codec_i2s_out_pointer .pointer = snd_azf3328_pcm_pointer
}; };
static int __devinit 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); snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
delay = 49; /* minimum time is 49 ticks */ 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; delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay); snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
@ -2180,6 +2174,7 @@ snd_azf3328_create(struct snd_card *card,
}; };
u8 dma_init; u8 dma_init;
enum snd_azf3328_codec_type codec_type; enum snd_azf3328_codec_type codec_type;
struct snd_azf3328_codec_data *codec_setup;
*rchip = NULL; *rchip = NULL;
@ -2217,15 +2212,23 @@ snd_azf3328_create(struct snd_card *card,
chip->opl3_io = pci_resource_start(pci, 3); chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4); chip->mixer_io = pci_resource_start(pci, 4);
chip->codecs[AZF_CODEC_PLAYBACK].io_base = codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK; codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK"; codec_setup->lock = &chip->reg_lock;
chip->codecs[AZF_CODEC_CAPTURE].io_base = codec_setup->type = AZF_CODEC_PLAYBACK;
chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE; codec_setup->name = "PLAYBACK";
chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
chip->codecs[AZF_CODEC_I2S_OUT].io_base = codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT; codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT"; 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, if (request_irq(pci->irq, snd_azf3328_interrupt,
IRQF_SHARED, card->shortname, chip)) { IRQF_SHARED, card->shortname, chip)) {
@ -2257,15 +2260,15 @@ snd_azf3328_create(struct snd_card *card,
struct snd_azf3328_codec_data *codec = struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type]; &chip->codecs[codec_type];
/* shutdown codecs to save power */ /* shutdown codecs to reduce power / noise */
/* have ...ctrl_codec_activity() act properly */ /* have ...ctrl_codec_activity() act properly */
codec->running = 1; codec->running = 1;
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0); 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, snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init); dma_init);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(codec->lock);
} }
snd_card_set_dev(card, &pci->dev); 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); 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_PLAYBACK]);
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]); 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, static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info) 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; return snd_ctl_enum_info(info, 1, 3, texts);
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item > 2)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
return 0;
} }
static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol, 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 snd_ctl_elem_info *uinfo)
{ {
struct cmipci *cm = snd_kcontrol_chip(kcontrol); struct cmipci *cm = snd_kcontrol_chip(kcontrol);
static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" }; static const char *const texts[3] = {
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; "Line-In", "Rear Output", "Bass Output"
uinfo->count = 1; };
uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) return snd_ctl_enum_info(uinfo, 1,
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; cm->chip_version >= 39 ? 3 : 2, texts);
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
} }
static inline unsigned int get_line_in_mode(struct cmipci *cm) 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, static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
static char *texts[2] = { "Mic-In", "Center/LFE Output" }; static const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1; return snd_ctl_enum_info(uinfo, 1, 2, texts);
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 int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol, 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; pos_adj = 0;
} else { } else {
ofs = setup_bdle(substream, azx_dev, ofs = setup_bdle(substream, azx_dev,
&bdl, ofs, pos_adj, 1); &bdl, ofs, pos_adj,
!substream->runtime->no_period_wakeup);
if (ofs < 0) if (ofs < 0)
goto error; goto error;
} }
@ -1247,7 +1248,8 @@ static int azx_setup_periods(struct azx *chip,
period_bytes - pos_adj, 0); period_bytes - pos_adj, 0);
else else
ofs = setup_bdle(substream, azx_dev, &bdl, ofs, ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
period_bytes, 1); period_bytes,
!substream->runtime->no_period_wakeup);
if (ofs < 0) if (ofs < 0)
goto error; goto error;
} }
@ -1515,7 +1517,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* No full-resume yet implemented */ /* No full-resume yet implemented */
/* SNDRV_PCM_INFO_RESUME |*/ /* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE | 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, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000, .rate_min = 48000,

View File

@ -10857,6 +10857,9 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
return 0; 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... */ /* almost identical with ALC880 parser... */
static int alc882_parse_auto_config(struct hda_codec *codec) 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); err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
if (err < 0) if (err < 0)
return err; 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) if (err < 0)
return err; return err;
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], 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) #define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
/* add playback controls from the parsed DAC table */ /* 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 */ * different NIDs for mute/unmute switch and volume control */
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg) const struct auto_pin_cfg *cfg)

View File

@ -263,8 +263,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
return; return;
snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
!spec->vt1708_jack_detectect); !spec->vt1708_jack_detectect);
cancel_delayed_work(&spec->vt1708_hp_work); cancel_delayed_work_sync(&spec->vt1708_hp_work);
flush_scheduled_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_CCLK | ICE1712_DELTA_AP_CS_CODEC;
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL; tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
break; 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: case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B; tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
tmp &= ~ICE1712_VX442_CS_DIGITAL; 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: case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CS_DIGITAL; tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
break; break;
case ICE1712_SUBDEVICE_DELTA66E:
tmp |= ICE1712_DELTA_66E_CS_CS8427;
break;
case ICE1712_SUBDEVICE_VX442: case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CS_DIGITAL; tmp |= ICE1712_VX442_CS_DIGITAL;
break; break;
@ -275,6 +283,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
priv->cs_addr = chip << 4; 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 * AK4528 on VX442 to choose the chip mask
*/ */
@ -487,6 +509,29 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.mask_flags = 0, .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 = { static struct snd_akm4xxx akm_delta44 __devinitdata = {
.type = SND_AK4524, .type = SND_AK4524,
.num_adcs = 4, .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); err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break; break;
case ICE1712_SUBDEVICE_VX442: case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice); err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break; break;
case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
break;
default: default:
snd_BUG(); snd_BUG();
return -EINVAL; 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_CS_NONE 0x50 /* nothing */
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */ #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 */ /* Digigram VX442 definitions */
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */ #define ICE1712_VX442_CCLK 0x02 /* SPI clock */
#define ICE1712_VX442_DIN 0x04 /* data input */ #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-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 xonar_dg.o
snd-oxygen-objs := oxygen.o
snd-virtuoso-objs := virtuoso.o xonar_lib.o \ snd-virtuoso-objs := virtuoso.o xonar_lib.o \
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.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_OXYGEN_LIB) += snd-oxygen-lib.o
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.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: * CMI8788:
* *
* SPI 0 -> 1st AK4396 (front) * SPI 0 -> 1st AK4396 (front)
* SPI 1 -> 2nd AK4396 (surround) * SPI 1 -> 2nd AK4396 (surround)
* SPI 2 -> 3rd AK4396 (center/LFE) * SPI 2 -> 3rd AK4396 (center/LFE)
* SPI 3 -> WM8785 * SPI 3 -> WM8785
* SPI 4 -> 4th AK4396 (back) * SPI 4 -> 4th AK4396 (back)
* *
* GPIO 0 -> DFS0 of AK5385 * GPIO 0 -> DFS0 of AK5385
* GPIO 1 -> DFS1 of AK5385 * GPIO 1 -> DFS1 of AK5385
* GPIO 8 -> enable headphone amplifier on HT-Omega models *
* 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: * 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> #include <linux/delay.h>
@ -41,18 +54,22 @@
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include "oxygen.h" #include "oxygen.h"
#include "xonar_dg.h"
#include "ak4396.h" #include "ak4396.h"
#include "wm8785.h" #include "wm8785.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver"); MODULE_DESCRIPTION("C-Media CMI8788 driver");
MODULE_LICENSE("GPL v2"); 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 int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 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"); MODULE_PARM_DESC(enable, "enable card");
enum { enum {
MODEL_CMEDIA_REF, /* C-Media's reference design */ MODEL_CMEDIA_REF,
MODEL_MERIDIAN, /* AuzenTech X-Meridian */ MODEL_MERIDIAN,
MODEL_CLARO, /* HT-Omega Claro */ MODEL_MERIDIAN_2G,
MODEL_CLARO_HALO, /* HT-Omega Claro halo */ MODEL_CLARO,
MODEL_CLARO_HALO,
MODEL_FANTASIA,
MODEL_SERENADE,
MODEL_2CH_OUTPUT,
MODEL_HG2PCI,
MODEL_XONAR_DG,
}; };
static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = { 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, 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, 0x0218), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .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, 0x0001), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .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, 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(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .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 }, { 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 }, { OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
/* HT-Omega Claro halo */
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_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_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002 #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 #define GPIO_CLARO_HP 0x0100
struct generic_data { struct generic_data {
unsigned int dacs;
u8 ak4396_regs[4][5]; u8 ak4396_regs[4][5];
u16 wm8785_regs[3]; u16 wm8785_regs[3];
}; };
@ -148,7 +193,7 @@ static void ak4396_registers_init(struct oxygen *chip)
struct generic_data *data = chip->model_data; struct generic_data *data = chip->model_data;
unsigned int i; unsigned int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < data->dacs; ++i) {
ak4396_write(chip, i, AK4396_CONTROL_1, ak4396_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB | AK4396_RSTN); AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i, AK4396_CONTROL_2, 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; struct generic_data *data = chip->model_data;
data->dacs = chip->model.dac_channels_pcm / 2;
data->ak4396_regs[0][AK4396_CONTROL_2] = data->ak4396_regs[0][AK4396_CONTROL_2] =
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
ak4396_registers_init(chip); ak4396_registers_init(chip);
@ -207,6 +253,10 @@ static void generic_init(struct oxygen *chip)
static void meridian_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); ak4396_init(chip);
ak5385_init(chip); ak5385_init(chip);
} }
@ -220,6 +270,8 @@ static void claro_enable_hp(struct oxygen *chip)
static void claro_init(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); ak4396_init(chip);
wm8785_init(chip); wm8785_init(chip);
claro_enable_hp(chip); claro_enable_hp(chip);
@ -227,11 +279,24 @@ static void claro_init(struct oxygen *chip)
static void claro_halo_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); ak4396_init(chip);
ak5385_init(chip); ak5385_init(chip);
claro_enable_hp(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) static void generic_cleanup(struct oxygen *chip)
{ {
} }
@ -268,6 +333,11 @@ static void claro_resume(struct oxygen *chip)
claro_enable_hp(chip); claro_enable_hp(chip);
} }
static void stereo_resume(struct oxygen *chip)
{
ak4396_registers_init(chip);
}
static void set_ak4396_params(struct oxygen *chip, static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params) 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 */ msleep(1); /* wait for the new MCLK to become stable */
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) { 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_write(chip, i, AK4396_CONTROL_1,
AK4396_DIF_24_MSB); AK4396_DIF_24_MSB);
ak4396_write(chip, i, AK4396_CONTROL_2, value); 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) static void update_ak4396_volume(struct oxygen *chip)
{ {
struct generic_data *data = chip->model_data;
unsigned int i; unsigned int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < data->dacs; ++i) {
ak4396_write_cached(chip, i, AK4396_LCH_ATT, ak4396_write_cached(chip, i, AK4396_LCH_ATT,
chip->dac_volume[i * 2]); chip->dac_volume[i * 2]);
ak4396_write_cached(chip, i, AK4396_RCH_ATT, 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; value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
if (chip->dac_mute) if (chip->dac_mute)
value |= AK4396_SMUTE; 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); 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); 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, static int rolloff_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info) struct snd_ctl_elem_info *info)
{ {
@ -363,13 +438,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Sharp Roll-off", "Slow Roll-off" "Sharp Roll-off", "Slow Roll-off"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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 rolloff_get(struct snd_kcontrol *ctl, static int rolloff_get(struct snd_kcontrol *ctl,
@ -400,7 +469,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
reg &= ~AK4396_SLOW; reg &= ~AK4396_SLOW;
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2]; changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
if (changed) { if (changed) {
for (i = 0; i < 4; ++i) for (i = 0; i < data->dacs; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, reg); ak4396_write(chip, i, AK4396_CONTROL_2, reg);
} }
mutex_unlock(&chip->mutex); 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" "None", "High-pass Filter"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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 hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 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, .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) static int generic_mixer_init(struct oxygen *chip)
{ {
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, 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; 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 DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static const struct oxygen_model model_generic = { static const struct oxygen_model model_generic = {
@ -494,11 +726,11 @@ static const struct oxygen_model model_generic = {
.mixer_init = generic_wm8785_mixer_init, .mixer_init = generic_wm8785_mixer_init,
.cleanup = generic_cleanup, .cleanup = generic_cleanup,
.resume = generic_resume, .resume = generic_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_ak4396_params, .set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params, .set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume, .update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute, .update_dac_mute = update_ak4396_mute,
.dump_registers = dump_oxygen_registers,
.dac_tlv = ak4396_db_scale, .dac_tlv = ak4396_db_scale,
.model_data_size = sizeof(struct generic_data), .model_data_size = sizeof(struct generic_data),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
@ -508,11 +740,14 @@ static const struct oxygen_model model_generic = {
CAPTURE_1_FROM_SPDIF | CAPTURE_1_FROM_SPDIF |
CAPTURE_2_FROM_AC97_1 | CAPTURE_2_FROM_AC97_1 |
AC97_CD_INPUT, AC97_CD_INPUT,
.dac_channels = 8, .dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 0, .dac_volume_min = 0,
.dac_volume_max = 255, .dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI | .function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5, 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, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_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, static int __devinit get_oxygen_model(struct oxygen *chip,
const struct pci_device_id *id) 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; chip->model = model_generic;
switch (id->driver_data) { switch (id->driver_data) {
case MODEL_MERIDIAN: case MODEL_MERIDIAN:
case MODEL_MERIDIAN_2G:
chip->model.init = meridian_init; 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.resume = meridian_resume;
chip->model.set_adc_params = set_ak5385_params; chip->model.set_adc_params = set_ak5385_params;
chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S | chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 | CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF; CAPTURE_1_FROM_SPDIF;
if (id->driver_data == MODEL_MERIDIAN)
chip->model.device_config |= AC97_CD_INPUT;
break; break;
case MODEL_CLARO: case MODEL_CLARO:
chip->model.init = claro_init; chip->model.init = claro_init;
chip->model.mixer_init = claro_mixer_init;
chip->model.cleanup = claro_cleanup; chip->model.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend; chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume; chip->model.resume = claro_resume;
break; break;
case MODEL_CLARO_HALO: case MODEL_CLARO_HALO:
chip->model.init = claro_halo_init; 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.cleanup = claro_cleanup;
chip->model.suspend = claro_suspend; chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume; chip->model.resume = claro_resume;
chip->model.set_adc_params = set_ak5385_params; chip->model.set_adc_params = set_ak5385_params;
chip->model.dump_registers = dump_ak4396_registers;
chip->model.device_config = PLAYBACK_0_TO_I2S | chip->model.device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 | CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF; CAPTURE_1_FROM_SPDIF;
break; 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 || if (id->driver_data == MODEL_MERIDIAN ||
id->driver_data == MODEL_MERIDIAN_2G ||
id->driver_data == MODEL_CLARO_HALO) { id->driver_data == MODEL_CLARO_HALO) {
chip->model.misc_flags = OXYGEN_MISC_MIDI; chip->model.misc_flags = OXYGEN_MISC_MIDI;
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT; 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; return 0;
} }

View File

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

View File

@ -197,11 +197,11 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
{ {
unsigned int count; 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; count = 10;
while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
&& count > 0) { && count > 0) {
udelay(1); udelay(4);
--count; --count;
} }

View File

@ -202,7 +202,13 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
struct oxygen *chip = entry->private_data; struct oxygen *chip = entry->private_data;
int i, j; 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) { for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
snd_iprintf(buffer, "%02x:", i); snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; ++j) 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) if (mutex_lock_interruptible(&chip->mutex) < 0)
return; return;
if (chip->has_ac97_0) { if (chip->has_ac97_0) {
snd_iprintf(buffer, "\nAC97\n"); snd_iprintf(buffer, "\nAC97:\n");
for (i = 0; i < 0x80; i += 0x10) { for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i); snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2) 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) { if (chip->has_ac97_1) {
snd_iprintf(buffer, "\nAC97 2\n"); snd_iprintf(buffer, "\nAC97 2:\n");
for (i = 0; i < 0x80; i += 0x10) { for (i = 0; i < 0x80; i += 0x10) {
snd_iprintf(buffer, "%02x:", i); snd_iprintf(buffer, "%02x:", i);
for (j = 0; j < 0x10; j += 2) 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); mutex_unlock(&chip->mutex);
if (chip->model.dump_registers)
chip->model.dump_registers(chip, buffer);
} }
static void oxygen_proc_init(struct oxygen *chip) static void oxygen_proc_init(struct oxygen *chip)
{ {
struct snd_info_entry *entry; 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); snd_info_set_text_ops(entry, chip, oxygen_proc_read);
} }
#else #else
@ -262,7 +270,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
*/ */
subdevice = oxygen_read_eeprom(chip, 2); subdevice = oxygen_read_eeprom(chip, 2);
/* use default ID if EEPROM is missing */ /* use default ID if EEPROM is missing */
if (subdevice == 0xffff) if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
subdevice = 0x8788; subdevice = 0x8788;
/* /*
* We use only the subsystem device ID for searching because it is * 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); (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
chip->spdif_pcm_bits = chip->spdif_bits; chip->spdif_pcm_bits = chip->spdif_bits;
if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2) if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
chip->revision = 2;
else
chip->revision = 1;
if (chip->revision == 1)
oxygen_set_bits8(chip, OXYGEN_MISC, oxygen_set_bits8(chip, OXYGEN_MISC,
OXYGEN_MISC_PCI_MEM_W_1_CLOCK); 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_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
OXYGEN_RATE_48000 | chip->model.dac_i2s_format | OXYGEN_RATE_48000 |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | chip->model.dac_i2s_format |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); 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) if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format | OXYGEN_RATE_48000 |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | chip->model.adc_i2s_format |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
else else
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, 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 | if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
CAPTURE_2_FROM_I2S_2)) CAPTURE_2_FROM_I2S_2))
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
OXYGEN_RATE_48000 | chip->model.adc_i2s_format | OXYGEN_RATE_48000 |
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | chip->model.adc_i2s_format |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
OXYGEN_I2S_BITS_16 |
OXYGEN_I2S_MASTER |
OXYGEN_I2S_BCLK_64);
else else
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, 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_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_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
OXYGEN_SPDIF_OUT_ENABLE | OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK); OXYGEN_SPDIF_LOOPBACK);
@ -557,7 +572,8 @@ static void oxygen_card_free(struct snd_card *card)
oxygen_shutdown(chip); oxygen_shutdown(chip);
if (chip->irq >= 0) if (chip->irq >= 0)
free_irq(chip->irq, chip); 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); chip->model.cleanup(chip);
kfree(chip->model_data); kfree(chip->model_data);
mutex_destroy(&chip->mutex); 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->driver, chip->model.chip);
strcpy(card->shortname, chip->model.shortname); strcpy(card->shortname, chip->model.shortname);
sprintf(card->longname, "%s (rev %u) at %#lx, irq %i", sprintf(card->longname, "%s at %#lx, irq %i",
chip->model.longname, chip->revision, chip->addr, chip->irq); chip->model.longname, chip->addr, chip->irq);
strcpy(card->mixername, chip->model.chip); strcpy(card->mixername, chip->model.chip);
snd_component_add(card, 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); spin_unlock_irq(&chip->reg_lock);
synchronize_irq(chip->irq); 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; chip->interrupt_mask = saved_interrupt_mask;
pci_disable_device(pci); 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; struct oxygen *chip = ctl->private_data;
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 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.min = chip->model.dac_volume_min;
info->value.integer.max = chip->model.dac_volume_max; info->value.integer.max = chip->model.dac_volume_max;
return 0; return 0;
@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
unsigned int i; unsigned int i;
mutex_lock(&chip->mutex); 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]; value->value.integer.value[i] = chip->dac_volume[i];
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
return 0; return 0;
@ -59,7 +59,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
changed = 0; changed = 0;
mutex_lock(&chip->mutex); 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]) { if (value->value.integer.value[i] != chip->dac_volume[i]) {
chip->dac_volume[i] = value->value.integer.value[i]; chip->dac_volume[i] = value->value.integer.value[i];
changed = 1; changed = 1;
@ -97,6 +97,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
return changed; 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 int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
{ {
static const char *const names[5] = { 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", "Front+Surround+Center/LFE+Back",
}; };
struct oxygen *chip = ctl->private_data; 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; return snd_ctl_enum_info(info, 1, count, names);
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;
} }
static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 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) static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
{ {
struct oxygen *chip = ctl->private_data; 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; int changed;
if (value->value.enumerated.item[0] >= count) if (value->value.enumerated.item[0] >= count)
@ -430,30 +434,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,
return 0; return 0;
} }
static int spdif_loopback_get(struct snd_kcontrol *ctl, static int spdif_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value) struct snd_ctl_elem_value *value)
{ {
struct oxygen *chip = ctl->private_data; struct oxygen *chip = ctl->private_data;
u32 bit = ctl->private_value;
value->value.integer.value[0] = value->value.integer.value[0] =
!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);
& OXYGEN_SPDIF_LOOPBACK);
return 0; return 0;
} }
static int spdif_loopback_put(struct snd_kcontrol *ctl, static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value) struct snd_ctl_elem_value *value)
{ {
struct oxygen *chip = ctl->private_data; struct oxygen *chip = ctl->private_data;
u32 bit = ctl->private_value;
u32 oldreg, newreg; u32 oldreg, newreg;
int changed; int changed;
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
if (value->value.integer.value[0]) if (value->value.integer.value[0])
newreg = oldreg | OXYGEN_SPDIF_LOOPBACK; newreg = oldreg | bit;
else else
newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK; newreg = oldreg & ~bit;
changed = newreg != oldreg; changed = newreg != oldreg;
if (changed) if (changed)
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg); oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
@ -644,6 +649,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
return change; 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, static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
struct snd_ctl_elem_info *info) 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, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH), .name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = spdif_loopback_get, .get = spdif_bit_switch_get,
.put = spdif_loopback_put, .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_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), 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_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1), AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
@ -970,7 +1031,10 @@ static int add_controls(struct oxygen *chip,
continue; continue;
} }
if (!strcmp(template.name, "Stereo Upmixing") && 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; continue;
if (!strncmp(template.name, "CD Capture ", 11) && if (!strncmp(template.name, "CD Capture ", 11) &&
!(chip->model.device_config & AC97_CD_INPUT)) !(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_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | 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 | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 | .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_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | 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 | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_32000 | .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_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | 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, .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000, .rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000, .rate_min = 48000,
@ -140,7 +143,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
runtime->hw.rate_min = 44100; runtime->hw.rate_min = 44100;
break; break;
case PCM_MULTICH: case PCM_MULTICH:
runtime->hw.channels_max = chip->model.dac_channels; runtime->hw.channels_max = chip->model.dac_channels_pcm;
break; break;
} }
if (chip->model.pcm_hardware_filter) 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) static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
{ {
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) 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; 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, static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) 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_REC_FORMAT_A_MASK);
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
chip->model.adc_i2s_format | chip->model.adc_i2s_format |
get_mclk(chip, PCM_A, hw_params) |
oxygen_i2s_bits(hw_params), oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK | OXYGEN_I2S_FORMAT_MASK |
@ -393,9 +405,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
if (!is_ac97) if (!is_ac97)
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
chip->model.get_i2s_mclk(chip, PCM_B,
hw_params) |
chip->model.adc_i2s_format | chip->model.adc_i2s_format |
get_mclk(chip, PCM_B, hw_params) |
oxygen_i2s_bits(hw_params), oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_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_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
oxygen_rate(hw_params) | oxygen_rate(hw_params) |
chip->model.dac_i2s_format | chip->model.dac_i2s_format |
chip->model.get_i2s_mclk(chip, PCM_MULTICH, get_mclk(chip, PCM_MULTICH, hw_params) |
hw_params) |
oxygen_i2s_bits(hw_params), oxygen_i2s_bits(hw_params),
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_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_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
oxygen_clear_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); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
spin_unlock_irq(&chip->reg_lock); spin_unlock_irq(&chip->reg_lock);
return 0; return 0;

View File

@ -139,9 +139,11 @@
#define OXYGEN_I2S_FORMAT_I2S 0x0000 #define OXYGEN_I2S_FORMAT_I2S 0x0000
#define OXYGEN_I2S_FORMAT_LJUST 0x0008 #define OXYGEN_I2S_FORMAT_LJUST 0x0008
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */ #define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
#define OXYGEN_I2S_MCLK_128 0x0000 #define OXYGEN_I2S_MCLK_SHIFT 4
#define OXYGEN_I2S_MCLK_256 0x0010 #define MCLK_128 0
#define OXYGEN_I2S_MCLK_512 0x0020 #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_MASK 0x00c0
#define OXYGEN_I2S_BITS_16 0x0000 #define OXYGEN_I2S_BITS_16 0x0000
#define OXYGEN_I2S_BITS_20 0x0040 #define OXYGEN_I2S_BITS_20 0x0040
@ -238,11 +240,11 @@
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02 #define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
#define OXYGEN_SPI_DATA_LENGTH_2 0x00 #define OXYGEN_SPI_DATA_LENGTH_2 0x00
#define OXYGEN_SPI_DATA_LENGTH_3 0x02 #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_160 0x00 /* ns */
#define OXYGEN_SPI_CLOCK_320 0x40 #define OXYGEN_SPI_CLOCK_320 0x04
#define OXYGEN_SPI_CLOCK_640 0x80 #define OXYGEN_SPI_CLOCK_640 0x08
#define OXYGEN_SPI_CLOCK_1280 0xc0 #define OXYGEN_SPI_CLOCK_1280 0x0c
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */ #define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
#define OXYGEN_SPI_CODEC_SHIFT 4 #define OXYGEN_SPI_CODEC_SHIFT 4
#define OXYGEN_SPI_CEN_MASK 0x80 #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_init_cs53x1(struct oxygen *chip);
void xonar_set_cs53x1_params(struct oxygen *chip, void xonar_set_cs53x1_params(struct oxygen *chip,
struct snd_pcm_hw_params *params); struct snd_pcm_hw_params *params);
#define XONAR_GPIO_BIT_INVERT (1 << 16)
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
struct snd_ctl_elem_value *value); struct snd_ctl_elem_value *value);
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,

View File

@ -22,29 +22,28 @@
* *
* CMI8788: * CMI8788:
* *
* I²C <-> CS4398 (front) * I²C <-> CS4398 (addr 1001111) (front)
* <-> CS4362A (surround, center/LFE, back) * <-> 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 0 -> enable output to speakers
* GPIO 1 -> enable front panel I/O * GPIO 1 -> route output to front panel
* GPIO 2 -> M0 of CS5361 * GPIO 2 -> M0 of CS5361
* GPIO 3 -> M1 of CS5361 * GPIO 3 -> M1 of CS5361
* GPIO 8 -> route input jack to line-in (0) or mic-in (1) * GPIO 6 -> ?
* * GPIO 7 -> ?
* CS4398: * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
*
* AD0 <- 1
* AD1 <- 1
*
* CS4362A:
*
* AD0 <- 0
* *
* CM9780: * 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> #include <linux/pci.h>
@ -63,6 +62,7 @@
#define GPI_EXT_POWER 0x01 #define GPI_EXT_POWER 0x01
#define GPIO_D1_OUTPUT_ENABLE 0x0001 #define GPIO_D1_OUTPUT_ENABLE 0x0001
#define GPIO_D1_FRONT_PANEL 0x0002 #define GPIO_D1_FRONT_PANEL 0x0002
#define GPIO_D1_MAGIC 0x00c0
#define GPIO_D1_INPUT_ROUTE 0x0100 #define GPIO_D1_INPUT_ROUTE 0x0100
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ #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); cs43xx_registers_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 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, oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
xonar_init_cs53x1(chip); xonar_init_cs53x1(chip);
xonar_enable_output(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 = { static const struct snd_kcontrol_new front_panel_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Front Panel Switch", .name = "Front Panel Playback Switch",
.info = snd_ctl_boolean_mono_info, .info = snd_ctl_boolean_mono_info,
.get = xonar_gpio_bit_switch_get, .get = xonar_gpio_bit_switch_get,
.put = xonar_gpio_bit_switch_put, .put = xonar_gpio_bit_switch_put,
@ -298,13 +298,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
"Fast Roll-off", "Slow Roll-off" "Fast Roll-off", "Slow Roll-off"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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 rolloff_get(struct snd_kcontrol *ctl, static int rolloff_get(struct snd_kcontrol *ctl,
@ -380,6 +374,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
return 0; 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 = { static const struct oxygen_model model_xonar_d1 = {
.longname = "Asus Virtuoso 100", .longname = "Asus Virtuoso 100",
.chip = "AV200", .chip = "AV200",
@ -388,22 +406,26 @@ static const struct oxygen_model model_xonar_d1 = {
.cleanup = xonar_d1_cleanup, .cleanup = xonar_d1_cleanup,
.suspend = xonar_d1_suspend, .suspend = xonar_d1_suspend,
.resume = xonar_d1_resume, .resume = xonar_d1_resume,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_cs43xx_params, .set_dac_params = set_cs43xx_params,
.set_adc_params = xonar_set_cs53x1_params, .set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_cs43xx_volume, .update_dac_volume = update_cs43xx_volume,
.update_dac_mute = update_cs43xx_mute, .update_dac_mute = update_cs43xx_mute,
.update_center_lfe_mix = update_cs43xx_center_lfe_mix, .update_center_lfe_mix = update_cs43xx_center_lfe_mix,
.ac97_switch = xonar_d1_line_mic_ac97_switch, .ac97_switch = xonar_d1_line_mic_ac97_switch,
.dump_registers = dump_d1_registers,
.dac_tlv = cs4362a_db_scale, .dac_tlv = cs4362a_db_scale,
.model_data_size = sizeof(struct xonar_cs43xx), .model_data_size = sizeof(struct xonar_cs43xx),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2, CAPTURE_0_FROM_I2S_2 |
.dac_channels = 8, AC97_FMIC_SWITCH,
.dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 127 - 60, .dac_volume_min = 127 - 60,
.dac_volume_max = 127, .dac_volume_max = 127,
.function_flags = OXYGEN_FUNCTION_2WIRE, .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, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_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> * 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; struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value; u16 bit = ctl->private_value;
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
value->value.integer.value[0] = value->value.integer.value[0] =
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
return 0; return 0;
} }
@ -115,12 +116,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
{ {
struct oxygen *chip = ctl->private_data; struct oxygen *chip = ctl->private_data;
u16 bit = ctl->private_value; u16 bit = ctl->private_value;
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
u16 old_bits, new_bits; u16 old_bits, new_bits;
int changed; int changed;
spin_lock_irq(&chip->reg_lock); spin_lock_irq(&chip->reg_lock);
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); 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; new_bits = old_bits | bit;
else else
new_bits = old_bits & ~bit; new_bits = old_bits & ~bit;

View File

@ -22,20 +22,26 @@
* *
* CMI8788: * CMI8788:
* *
* SPI 0 -> 1st PCM1796 (front) * SPI 0 -> 1st PCM1796 (front)
* SPI 1 -> 2nd PCM1796 (surround) * SPI 1 -> 2nd PCM1796 (surround)
* SPI 2 -> 3rd PCM1796 (center/LFE) * SPI 2 -> 3rd PCM1796 (center/LFE)
* SPI 4 -> 4th PCM1796 (back) * SPI 4 -> 4th PCM1796 (back)
* *
* GPIO 2 -> M0 of CS5381 * GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381 * GPIO 3 -> M1 of CS5381
* GPIO 5 <- external power present (D2X only) * GPIO 5 <- external power present (D2X only)
* GPIO 7 -> ALT * GPIO 7 -> ALT
* GPIO 8 -> enable output to speakers * GPIO 8 -> enable output to speakers
* *
* CM9780: * 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: * 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 0 -> enable HDMI (0) or speaker (1) output
* GPIO 2 -> M0 of CS5381 * GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381 * GPIO 3 -> M1 of CS5381
* GPIO 8 -> route input jack to line-in (0) or mic-in (1) * 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 * UART <-> HDMI controller
* RXD <- HDMI controller
*
* PCM1796 front: AD1,0 <- 0,0
* *
* CM9780: * 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 * no daughterboard
* ---------------- * ----------------
* *
* GPIO 4 <- 1 * GPIO 4 <- 1
* *
* H6 daughterboard * H6 daughterboard
* ---------------- * ----------------
* *
* GPIO 4 <- 0 * GPIO 4 <- 0
* GPIO 5 <- 0 * GPIO 5 <- 0
* *
* I²C <-> PCM1796 (surround) * I²C <-> PCM1796 (addr 1001101) (surround)
* <-> PCM1796 (center/LFE) * <-> PCM1796 (addr 1001110) (center/LFE)
* <-> PCM1796 (back) * <-> PCM1796 (addr 1001111) (back)
*
* PCM1796 surround: AD1,0 <- 0,1
* PCM1796 center/LFE: AD1,0 <- 1,0
* PCM1796 back: AD1,0 <- 1,1
* *
* unknown daughterboard * unknown daughterboard
* --------------------- * ---------------------
* *
* GPIO 4 <- 0 * GPIO 4 <- 0
* GPIO 5 <- 1 * GPIO 5 <- 1
* *
* I²C <-> CS4362A (surround, center/LFE, back) * I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)
*
* CS4362A: AD0 <- 0
*/ */
/* /*
@ -98,32 +105,35 @@
* *
* CMI8788: * CMI8788:
* *
* I²C <-> PCM1792A * I²C <-> PCM1792A (addr 1001100)
* <-> CS2000 (ST only) * <-> 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 0 -> enable output to speakers
* GPIO 1 -> route HP to front panel (0) or rear jack (1) * GPIO 1 -> route HP to front panel (0) or rear jack (1)
* GPIO 2 -> M0 of CS5381 * GPIO 2 -> M0 of CS5381
* GPIO 3 -> M1 of CS5381 * GPIO 3 -> M1 of CS5381
* GPIO 7 -> route output to speaker jacks (0) or HP (1) * GPIO 4 <- daughterboard detection
* GPIO 8 -> route input jack to line-in (0) or mic-in (1) * 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: * PCM1792A:
* *
* AD1,0 <- 0,0 * SCK <- CLK_OUT of CS2000 (ST only)
* SCK <- CLK_OUT of CS2000 (ST only)
*
* CS2000:
*
* AD0 <- 0
* *
* CM9780: * 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 * H6 daughterboard
* ---------------- * ----------------
@ -133,15 +143,39 @@
*/ */
/* /*
* Xonar HDAV1.3 Slim * Xonar Xense
* ------------------ * -----------
* *
* CMI8788: * CMI8788:
* *
* GPIO 1 -> enable output * I²C <-> PCM1796 (addr 1001100) (front)
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
* <-> CS2000 (addr 1001110)
* *
* TXD -> HDMI controller * ADC1 MCLK -> REF_CLK of CS2000
* RXD <- HDMI controller *
* 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> #include <linux/pci.h>
@ -150,6 +184,7 @@
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/tlv.h> #include <sound/tlv.h>
@ -167,12 +202,14 @@
#define GPIO_INPUT_ROUTE 0x0100 #define GPIO_INPUT_ROUTE 0x0100
#define GPIO_HDAV_OUTPUT_ENABLE 0x0001 #define GPIO_HDAV_OUTPUT_ENABLE 0x0001
#define GPIO_HDAV_MAGIC 0x00c0
#define GPIO_DB_MASK 0x0030 #define GPIO_DB_MASK 0x0030
#define GPIO_DB_H6 0x0000 #define GPIO_DB_H6 0x0000
#define GPIO_ST_OUTPUT_ENABLE 0x0001 #define GPIO_ST_OUTPUT_ENABLE 0x0001
#define GPIO_ST_HP_REAR 0x0002 #define GPIO_ST_HP_REAR 0x0002
#define GPIO_ST_MAGIC 0x0040
#define GPIO_ST_HP 0x0080 #define GPIO_ST_HP 0x0080
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */
@ -186,11 +223,12 @@ struct xonar_pcm179x {
unsigned int dacs; unsigned int dacs;
u8 pcm1796_regs[4][5]; u8 pcm1796_regs[4][5];
unsigned int current_rate; unsigned int current_rate;
bool os_128; bool h6;
bool hp_active; bool hp_active;
s8 hp_gain_offset; s8 hp_gain_offset;
bool has_cs2000; bool has_cs2000;
u8 cs2000_fun_cfg_1; u8 cs2000_regs[0x1f];
bool broken_i2c;
}; };
struct xonar_hdav { 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; struct xonar_pcm179x *data = chip->model_data;
oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
if (reg == CS2000_FUN_CFG_1) data->cs2000_regs[reg] = value;
data->cs2000_fun_cfg_1 = value;
} }
static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value) static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
{ {
struct xonar_pcm179x *data = chip->model_data; struct xonar_pcm179x *data = chip->model_data;
if (reg != CS2000_FUN_CFG_1 || if (value != data->cs2000_regs[reg])
value != data->cs2000_fun_cfg_1)
cs2000_write(chip, reg, value); cs2000_write(chip, reg, value);
} }
@ -268,6 +304,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
unsigned int i; unsigned int i;
s8 gain_offset; s8 gain_offset;
msleep(1);
gain_offset = data->hp_active ? data->hp_gain_offset : 0; gain_offset = data->hp_active ? data->hp_gain_offset : 0;
for (i = 0; i < data->dacs; ++i) { for (i = 0; i < data->dacs; ++i) {
/* set ATLD before ATL/ATR */ /* set ATLD before ATL/ATR */
@ -282,6 +319,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
pcm1796_write(chip, i, 20, pcm1796_write(chip, i, 20,
data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
pcm1796_write(chip, i, 21, 0); 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; struct xonar_pcm179x *data = chip->model_data;
data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | 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] = data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
PCM1796_FLT_SHARP | PCM1796_ATS_1; 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); pcm1796_registers_init(chip);
data->current_rate = 48000; data->current_rate = 48000;
} }
@ -339,18 +378,20 @@ static void xonar_hdav_init(struct oxygen *chip)
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_FAST); OXYGEN_2WIRE_SPEED_STANDARD);
data->pcm179x.generic.anti_pop_delay = 100; data->pcm179x.generic.anti_pop_delay = 100;
data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE; data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA; 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_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; 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); 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); oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
xonar_init_cs53x1(chip); 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_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_FAST); OXYGEN_2WIRE_SPEED_STANDARD);
} }
static void xonar_st_init_common(struct oxygen *chip) 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; struct xonar_pcm179x *data = chip->model_data;
data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; 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; data->hp_gain_offset = 2*-18;
pcm1796_init(chip); pcm1796_init(chip);
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, 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, oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); 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 + 1, 0x10);
cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00); cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
cs2000_write(chip, CS2000_RATIO_0 + 3, 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_FUN_CFG_2, 0);
cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2); cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
msleep(3); /* PLL lock delay */
} }
static void xonar_st_init(struct oxygen *chip) 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; struct xonar_pcm179x *data = chip->model_data;
data->generic.anti_pop_delay = 100; data->generic.anti_pop_delay = 100;
data->h6 = chip->model.dac_channels_mixer > 2;
data->has_cs2000 = 1; 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_write16(chip, OXYGEN_I2S_A_FORMAT,
OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S | OXYGEN_RATE_48000 |
OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | OXYGEN_I2S_FORMAT_I2S |
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); 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); xonar_st_init_i2c(chip);
cs2000_registers_init(chip); cs2000_registers_init(chip);
@ -507,44 +556,16 @@ static void xonar_st_resume(struct oxygen *chip)
xonar_stx_resume(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) static void update_pcm1796_oversampling(struct oxygen *chip)
{ {
struct xonar_pcm179x *data = chip->model_data; struct xonar_pcm179x *data = chip->model_data;
unsigned int i; unsigned int i;
u8 reg; u8 reg;
if (data->current_rate <= 32000) if (data->current_rate <= 48000 && !data->h6)
reg = PCM1796_OS_128; 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 else
reg = PCM1796_OS_32; reg = PCM1796_OS_64;
for (i = 0; i < data->dacs; ++i) for (i = 0; i < data->dacs; ++i)
pcm1796_write_cached(chip, i, 20, reg); 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; struct xonar_pcm179x *data = chip->model_data;
msleep(1);
data->current_rate = params_rate(params); data->current_rate = params_rate(params);
update_pcm1796_oversampling(chip); update_pcm1796_oversampling(chip);
} }
@ -570,6 +592,7 @@ static void update_pcm1796_volume(struct oxygen *chip)
+ gain_offset); + gain_offset);
pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1] pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
+ gain_offset); + gain_offset);
gain_offset = 0;
} }
} }
@ -579,7 +602,7 @@ static void update_pcm1796_mute(struct oxygen *chip)
unsigned int i; unsigned int i;
u8 value; 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) if (chip->dac_mute)
value |= PCM1796_MUTE; value |= PCM1796_MUTE;
for (i = 0; i < data->dacs; ++i) 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; u8 rate_mclk, reg;
switch (rate) { switch (rate) {
/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
case 32000: case 32000:
rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; case 64000:
rate_mclk = OXYGEN_RATE_32000;
break; break;
case 44100: 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: 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: case 176400:
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; rate_mclk = OXYGEN_RATE_44100;
break; break;
default:
case 48000:
case 96000:
case 192000: case 192000:
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; rate_mclk = OXYGEN_RATE_48000;
break; 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_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); 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); cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
msleep(3); /* PLL lock delay */
} }
static void set_st_params(struct oxygen *chip, 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" "Sharp Roll-off", "Slow Roll-off"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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 rolloff_get(struct snd_kcontrol *ctl, static int rolloff_get(struct snd_kcontrol *ctl,
@ -719,57 +726,13 @@ static const struct snd_kcontrol_new rolloff_control = {
.put = rolloff_put, .put = rolloff_put,
}; };
static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) static const struct snd_kcontrol_new hdav_hdmi_control = {
{
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 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Oversampling Playback Enum", .name = "HDMI Playback Switch",
.info = os_128_info, .info = snd_ctl_boolean_mono_info,
.get = os_128_get, .get = xonar_gpio_bit_switch_get,
.put = os_128_put, .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, 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" "Speakers", "Headphones", "FP Headphones"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 3, names);
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;
} }
static int st_output_switch_get(struct snd_kcontrol *ctl, 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" "< 64 ohms", "64-300 ohms", "300-600 ohms"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 3, names);
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;
} }
static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, 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; 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) static int add_pcm1796_controls(struct oxygen *chip)
{ {
struct xonar_pcm179x *data = chip->model_data;
int err; int err;
err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); if (!data->broken_i2c) {
if (err < 0) err = snd_ctl_add(chip->card,
return err; snd_ctl_new1(&rolloff_control, chip));
err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); if (err < 0)
if (err < 0) return err;
return err; }
return 0; return 0;
} }
@ -956,7 +916,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)
static int xonar_hdav_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) static int xonar_st_mixer_init(struct oxygen *chip)
@ -976,6 +944,45 @@ static int xonar_st_mixer_init(struct oxygen *chip)
return 0; 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 = { static const struct oxygen_model model_xonar_d2 = {
.longname = "Asus Virtuoso 200", .longname = "Asus Virtuoso 200",
.chip = "AV200", .chip = "AV200",
@ -985,11 +992,11 @@ static const struct oxygen_model model_xonar_d2 = {
.cleanup = xonar_d2_cleanup, .cleanup = xonar_d2_cleanup,
.suspend = xonar_d2_suspend, .suspend = xonar_d2_suspend,
.resume = xonar_d2_resume, .resume = xonar_d2_resume,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_pcm1796_params, .set_dac_params = set_pcm1796_params,
.set_adc_params = xonar_set_cs53x1_params, .set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume, .update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute, .update_dac_mute = update_pcm1796_mute,
.dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale, .dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x), .model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
@ -999,13 +1006,16 @@ static const struct oxygen_model model_xonar_d2 = {
MIDI_OUTPUT | MIDI_OUTPUT |
MIDI_INPUT | MIDI_INPUT |
AC97_CD_INPUT, AC97_CD_INPUT,
.dac_channels = 8, .dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60, .dac_volume_min = 255 - 2*60,
.dac_volume_max = 255, .dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI, .misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_SPI | .function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5, 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, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
}; };
@ -1018,25 +1028,28 @@ static const struct oxygen_model model_xonar_hdav = {
.suspend = xonar_hdav_suspend, .suspend = xonar_hdav_suspend,
.resume = xonar_hdav_resume, .resume = xonar_hdav_resume,
.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_hdav_params, .set_dac_params = set_hdav_params,
.set_adc_params = xonar_set_cs53x1_params, .set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume, .update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute, .update_dac_mute = update_pcm1796_mute,
.uart_input = xonar_hdmi_uart_input, .uart_input = xonar_hdmi_uart_input,
.ac97_switch = xonar_line_mic_ac97_switch, .ac97_switch = xonar_line_mic_ac97_switch,
.dump_registers = dump_pcm1796_registers,
.dac_tlv = pcm1796_db_scale, .dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_hdav), .model_data_size = sizeof(struct xonar_hdav),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 | CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF, CAPTURE_1_FROM_SPDIF,
.dac_channels = 8, .dac_channels_pcm = 8,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60, .dac_volume_min = 255 - 2*60,
.dac_volume_max = 255, .dac_volume_max = 255,
.misc_flags = OXYGEN_MISC_MIDI, .misc_flags = OXYGEN_MISC_MIDI,
.function_flags = OXYGEN_FUNCTION_2WIRE, .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, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
}; };
@ -1048,22 +1061,26 @@ static const struct oxygen_model model_xonar_st = {
.cleanup = xonar_st_cleanup, .cleanup = xonar_st_cleanup,
.suspend = xonar_st_suspend, .suspend = xonar_st_suspend,
.resume = xonar_st_resume, .resume = xonar_st_resume,
.get_i2s_mclk = get_pcm1796_i2s_mclk,
.set_dac_params = set_st_params, .set_dac_params = set_st_params,
.set_adc_params = xonar_set_cs53x1_params, .set_adc_params = xonar_set_cs53x1_params,
.update_dac_volume = update_pcm1796_volume, .update_dac_volume = update_pcm1796_volume,
.update_dac_mute = update_pcm1796_mute, .update_dac_mute = update_pcm1796_mute,
.ac97_switch = xonar_line_mic_ac97_switch, .ac97_switch = xonar_line_mic_ac97_switch,
.dump_registers = dump_st_registers,
.dac_tlv = pcm1796_db_scale, .dac_tlv = pcm1796_db_scale,
.model_data_size = sizeof(struct xonar_pcm179x), .model_data_size = sizeof(struct xonar_pcm179x),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2, CAPTURE_0_FROM_I2S_2 |
.dac_channels = 2, AC97_FMIC_SWITCH,
.dac_channels_pcm = 2,
.dac_channels_mixer = 2,
.dac_volume_min = 255 - 2*60, .dac_volume_min = 255 - 2*60,
.dac_volume_max = 255, .dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_2WIRE, .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, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
}; };
@ -1089,7 +1106,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break; break;
case GPIO_DB_H6: case GPIO_DB_H6:
chip->model.shortname = "Xonar HDAV1.3+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;
} }
break; break;
@ -1102,8 +1120,10 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
break; break;
case GPIO_DB_H6: case GPIO_DB_H6:
chip->model.shortname = "Xonar ST+H6"; chip->model.shortname = "Xonar ST+H6";
chip->model.dac_channels = 8; chip->model.control_filter = xonar_st_h6_control_filter;
chip->model.private_data = 1; chip->model.dac_channels_pcm = 8;
chip->model.dac_channels_mixer = 8;
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
break; break;
} }
break; break;
@ -1114,9 +1134,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume; chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params; chip->model.set_dac_params = set_pcm1796_params;
break; break;
case 0x835e:
snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
return -ENODEV;
default: default:
return -EINVAL; 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> * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* *
@ -22,26 +22,48 @@
* *
* CMI8788: * CMI8788:
* *
* SPI 0 -> WM8766 (surround, center/LFE, back) * SPI 0 -> WM8766 (surround, center/LFE, back)
* SPI 1 -> WM8776 (front, input) * SPI 1 -> WM8776 (front, input)
* *
* GPIO 4 <- headphone detect, 0 = plugged * GPIO 4 <- headphone detect, 0 = plugged
* GPIO 6 -> route input jack to mic-in (0) or line-in (1) * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
* GPIO 7 -> enable output to front L/R speaker channels * GPIO 7 -> enable output to front L/R speaker channels
* GPIO 8 -> enable output to other speaker channels and front panel headphone * GPIO 8 -> enable output to other speaker channels and front panel headphone
* *
* WM8766: * WM8776:
* *
* input 1 <- line * input 1 <- line
* input 2 <- mic * input 2 <- mic
* input 3 <- front mic * input 3 <- front mic
* input 4 <- aux * 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/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h>
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
@ -55,6 +77,13 @@
#define GPIO_DS_OUTPUT_FRONTLR 0x0080 #define GPIO_DS_OUTPUT_FRONTLR 0x0080
#define GPIO_DS_OUTPUT_ENABLE 0x0100 #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_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000 #define LC_CONTROL_ALC 0x20000000
@ -66,19 +95,37 @@ struct xonar_wm87x6 {
struct snd_kcontrol *mic_adcmux_control; struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13]; struct snd_kcontrol *lc_controls[13];
struct snd_jack *hp_jack; struct snd_jack *hp_jack;
struct xonar_hdmi hdmi;
}; };
static void wm8776_write(struct oxygen *chip, static void wm8776_write_spi(struct oxygen *chip,
unsigned int reg, unsigned int value) unsigned int reg, unsigned int value)
{ {
struct xonar_wm87x6 *data = chip->model_data;
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_DATA_LENGTH_2 |
OXYGEN_SPI_CLOCK_160 | OXYGEN_SPI_CLOCK_160 |
(1 << OXYGEN_SPI_CODEC_SHIFT) | (1 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO, OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value); (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 < ARRAY_SIZE(data->wm8776_regs)) {
if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
value &= ~WM8776_UPDATE; value &= ~WM8776_UPDATE;
@ -245,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
snd_component_add(chip->card, "WM8766"); 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) static void xonar_ds_cleanup(struct oxygen *chip)
{ {
xonar_disable_output(chip); xonar_disable_output(chip);
wm8776_write(chip, WM8776_RESET, 0); 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) static void xonar_ds_suspend(struct oxygen *chip)
{ {
xonar_ds_cleanup(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) static void xonar_ds_resume(struct oxygen *chip)
{ {
wm8776_registers_init(chip); wm8776_registers_init(chip);
@ -264,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
xonar_ds_handle_hp_jack(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, static void wm8776_adc_hardware_filter(unsigned int channel,
struct snd_pcm_hardware *hardware) 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, static void set_wm87x6_dac_params(struct oxygen *chip,
struct snd_pcm_hw_params *params) 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); 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) static void update_wm8776_volume(struct oxygen *chip)
{ {
struct xonar_wm87x6 *data = chip->model_data; 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; const char *const *names;
max = (ctl->private_value >> 12) & 0xf; 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) { switch ((ctl->private_value >> 24) & 0x1f) {
case WM8776_ALCCTRL2: case WM8776_ALCCTRL2:
names = hld; names = hld;
@ -501,8 +600,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
default: default:
return -ENXIO; return -ENXIO;
} }
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); return snd_ctl_enum_info(info, 1, max + 1, names);
return 0;
} }
static int wm8776_field_volume_info(struct snd_kcontrol *ctl, 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] = { static const char *const names[3] = {
"None", "Peak Limiter", "Automatic Level Control" "None", "Peak Limiter", "Automatic Level Control"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = 1; return snd_ctl_enum_info(info, 1, 3, names);
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;
} }
static int wm8776_level_control_get(struct snd_kcontrol *ctl, 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" "None", "High-pass Filter"
}; };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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 hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 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, .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[] = { static const struct snd_kcontrol_new lc_controls[] = {
WM8776_FIELD_CTL_VOLUME("Limiter Threshold", WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, 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), 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) static int xonar_ds_mixer_init(struct oxygen *chip)
{ {
struct xonar_wm87x6 *data = chip->model_data; 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) if (!data->line_adcmux_control || !data->mic_adcmux_control)
return -ENXIO; return -ENXIO;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { return add_lc_controls(chip);
ctl = snd_ctl_new1(&lc_controls[i], 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) if (!ctl)
return -ENOMEM; return -ENOMEM;
err = snd_ctl_add(chip->card, ctl); err = snd_ctl_add(chip->card, ctl);
if (err < 0) if (err < 0)
return err; 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 = { static const struct oxygen_model model_xonar_ds = {
@ -1072,22 +1263,57 @@ static const struct oxygen_model model_xonar_ds = {
.suspend = xonar_ds_suspend, .suspend = xonar_ds_suspend,
.resume = xonar_ds_resume, .resume = xonar_ds_resume,
.pcm_hardware_filter = wm8776_adc_hardware_filter, .pcm_hardware_filter = wm8776_adc_hardware_filter,
.get_i2s_mclk = oxygen_default_i2s_mclk,
.set_dac_params = set_wm87x6_dac_params, .set_dac_params = set_wm87x6_dac_params,
.set_adc_params = set_wm8776_adc_params, .set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm87x6_volume, .update_dac_volume = update_wm87x6_volume,
.update_dac_mute = update_wm87x6_mute, .update_dac_mute = update_wm87x6_mute,
.update_center_lfe_mix = update_wm8766_center_lfe_mix, .update_center_lfe_mix = update_wm8766_center_lfe_mix,
.gpio_changed = xonar_ds_gpio_changed, .gpio_changed = xonar_ds_gpio_changed,
.dump_registers = dump_wm87x6_registers,
.dac_tlv = wm87x6_dac_db_scale, .dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6), .model_data_size = sizeof(struct xonar_wm87x6),
.device_config = PLAYBACK_0_TO_I2S | .device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF | PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_1, CAPTURE_0_FROM_I2S_1,
.dac_channels = 8, .dac_channels_pcm = 8,
.dac_channels_mixer = 8,
.dac_volume_min = 255 - 2*60, .dac_volume_min = 255 - 2*60,
.dac_volume_max = 255, .dac_volume_max = 255,
.function_flags = OXYGEN_FUNCTION_SPI, .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, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
.adc_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: case 0x838e:
chip->model = model_xonar_ds; chip->model = model_xonar_ds;
break; break;
case 0x835e:
chip->model = model_xonar_hdav_slim;
break;
default: default:
return -EINVAL; return -EINVAL;
} }

View File

@ -60,6 +60,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
"{RME HDSP-9652}," "{RME HDSP-9652},"
"{RME HDSP-9632}}"); "{RME HDSP-9632}}");
#ifdef HDSP_FW_LOADER #ifdef HDSP_FW_LOADER
MODULE_FIRMWARE("rpm_firmware.bin");
MODULE_FIRMWARE("multiface_firmware.bin"); MODULE_FIRMWARE("multiface_firmware.bin");
MODULE_FIRMWARE("multiface_firmware_rev11.bin"); MODULE_FIRMWARE("multiface_firmware_rev11.bin");
MODULE_FIRMWARE("digiface_firmware.bin"); MODULE_FIRMWARE("digiface_firmware.bin");
@ -81,6 +82,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define H9632_SS_CHANNELS 12 #define H9632_SS_CHANNELS 12
#define H9632_DS_CHANNELS 8 #define H9632_DS_CHANNELS 8
#define H9632_QS_CHANNELS 4 #define H9632_QS_CHANNELS 4
#define RPM_CHANNELS 6
/* Write registers. These are defined as byte-offsets from the iobase value. /* 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_PhoneGain1 (1<<30)
#define HDSP_QuadSpeed (1<<31) #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_ADGainMask (HDSP_ADGain0|HDSP_ADGain1)
#define HDSP_ADGainMinus10dBV HDSP_ADGainMask #define HDSP_ADGainMinus10dBV HDSP_ADGainMask
#define HDSP_ADGainPlus4dBu (HDSP_ADGain0) #define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
@ -450,7 +471,7 @@ struct hdsp {
u32 creg_spdif; u32 creg_spdif;
u32 creg_spdif_stream; u32 creg_spdif_stream;
int clock_source_locked; 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 */ enum HDSP_IO_Type io_type; /* ditto, but for code use */
unsigned short firmware_rev; unsigned short firmware_rev;
unsigned short state; /* stores state bits */ 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) { switch (hdsp->io_type) {
case Multiface: case Multiface:
case Digiface: case Digiface:
case RPM:
default: default:
if (hdsp->firmware_rev == 0xa) if (hdsp->firmware_rev == 0xa)
return (64 * out) + (32 + (in)); 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) { switch (hdsp->io_type) {
case Multiface: case Multiface:
case Digiface: case Digiface:
case RPM:
default: default:
if (hdsp->firmware_rev == 0xa) if (hdsp->firmware_rev == 0xa)
return (64 * out) + in; 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->io_type == H9652 || hdsp->io_type == H9632) return 0;
if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) { 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; hdsp->state &= ~HDSP_FirmwareLoaded;
return -EIO; 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; hdsp->state &= ~HDSP_FirmwareLoaded;
return -EIO; 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_control2Reg, HDSP_S_LOAD);
hdsp_write (hdsp, HDSP_fifoData, 0); hdsp_write (hdsp, HDSP_fifoData, 0);
if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) { 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_VERSION_BIT); hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD); if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT))
hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT); hdsp->io_type = RPM;
else
hdsp->io_type = Multiface;
} else { } else {
hdsp->io_type = Digiface; hdsp->io_type = Digiface;
} }
} else { } else {
/* firmware was already loaded, get iobox type */ /* 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; hdsp->io_type = Multiface;
else else
hdsp->io_type = Digiface; 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; hdsp->channel_map = channel_map_ds;
} else { } else {
switch (hdsp->io_type) { switch (hdsp->io_type) {
case RPM:
case Multiface: case Multiface:
hdsp->channel_map = channel_map_mf_ss; hdsp->channel_map = channel_map_mf_ss;
break; break;
@ -3231,6 +3259,318 @@ HDSP_PRECISE_POINTER("Precise Pointer", 0),
HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 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_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK; 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; int err;
struct snd_kcontrol *kctl; 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++) { 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) if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
return err; return err;
@ -3459,48 +3809,102 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
snd_iprintf(buffer, "\n"); snd_iprintf(buffer, "\n");
switch (hdsp_spdif_in(hdsp)) { if (hdsp->io_type != RPM) {
case HDSP_SPDIFIN_OPTICAL: switch (hdsp_spdif_in(hdsp)) {
snd_iprintf(buffer, "IEC958 input: Optical\n"); case HDSP_SPDIFIN_OPTICAL:
break; snd_iprintf(buffer, "IEC958 input: Optical\n");
case HDSP_SPDIFIN_COAXIAL: break;
snd_iprintf(buffer, "IEC958 input: Coaxial\n"); case HDSP_SPDIFIN_COAXIAL:
break; snd_iprintf(buffer, "IEC958 input: Coaxial\n");
case HDSP_SPDIFIN_INTERNAL: break;
snd_iprintf(buffer, "IEC958 input: Internal\n"); case HDSP_SPDIFIN_INTERNAL:
break; snd_iprintf(buffer, "IEC958 input: Internal\n");
case HDSP_SPDIFIN_AES: break;
snd_iprintf(buffer, "IEC958 input: AES\n"); case HDSP_SPDIFIN_AES:
break; snd_iprintf(buffer, "IEC958 input: AES\n");
default: break;
snd_iprintf(buffer, "IEC958 input: ???\n"); default:
break; snd_iprintf(buffer, "IEC958 input: ???\n");
break;
}
} }
if (hdsp->control_register & HDSP_SPDIFOpticalOut) if (RPM == hdsp->io_type) {
snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n"); if (hdsp->control_register & HDSP_RPM_Bypass)
else snd_iprintf(buffer, "RPM Bypass: disabled\n");
snd_iprintf(buffer, "IEC958 output: Coaxial only\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) switch (hdsp->control_register & HDSP_RPM_Inp12) {
snd_iprintf(buffer, "IEC958 quality: Professional\n"); case HDSP_RPM_Inp12_Phon_6dB:
else snd_iprintf(buffer, "Input 1/2: Phono, 6dB\n");
snd_iprintf(buffer, "IEC958 quality: Consumer\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) switch (hdsp->control_register & HDSP_RPM_Inp34) {
snd_iprintf(buffer, "IEC958 emphasis: on\n"); case HDSP_RPM_Inp34_Phon_6dB:
else snd_iprintf(buffer, "Input 3/4: Phono, 6dB\n");
snd_iprintf(buffer, "IEC958 emphasis: off\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) } else {
snd_iprintf(buffer, "IEC958 NonAudio: on\n"); if (hdsp->control_register & HDSP_SPDIFOpticalOut)
else snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
snd_iprintf(buffer, "IEC958 NonAudio: off\n"); else
if ((x = hdsp_spdif_sample_rate (hdsp)) != 0) snd_iprintf(buffer, "IEC958 output: Coaxial only\n");
snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
else
snd_iprintf (buffer, "IEC958 sample rate: Error flag set\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"); snd_iprintf(buffer, "\n");
/* Sync Check */ /* 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]); 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) { if (hdsp->use_midi_tasklet) {
/* we disable interrupts for this input until processing is done */ /* we disable interrupts for this input until processing is done */
hdsp->control_register &= ~HDSP_Midi1InterruptEnable; hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
@ -4093,7 +4497,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
SNDRV_PCM_RATE_96000), SNDRV_PCM_RATE_96000),
.rate_min = 32000, .rate_min = 32000,
.rate_max = 96000, .rate_max = 96000,
.channels_min = 14, .channels_min = 6,
.channels_max = HDSP_MAX_CHANNELS, .channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS, .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10, .period_bytes_min = (64 * 4) * 10,
@ -4122,7 +4526,7 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
SNDRV_PCM_RATE_96000), SNDRV_PCM_RATE_96000),
.rate_min = 32000, .rate_min = 32000,
.rate_max = 96000, .rate_max = 96000,
.channels_min = 14, .channels_min = 5,
.channels_max = HDSP_MAX_CHANNELS, .channels_max = HDSP_MAX_CHANNELS,
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS, .buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
.period_bytes_min = (64 * 4) * 10, .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, snd_hdsp_hw_rule_rate_out_channels, hdsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1); SNDRV_PCM_HW_PARAM_CHANNELS, -1);
hdsp->creg_spdif_stream = hdsp->creg_spdif; if (RPM != hdsp->io_type) {
hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; hdsp->creg_spdif_stream = hdsp->creg_spdif;
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE | hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id); snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
}
return 0; return 0;
} }
@ -4375,9 +4781,11 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
spin_unlock_irq(&hdsp->lock); spin_unlock_irq(&hdsp->lock);
hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; if (RPM != hdsp->io_type) {
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE | hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id); snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
}
return 0; 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) if (hdsp->io_type != H9632)
info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp); info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
info.spdif_sync_check = (unsigned char)hdsp_spdif_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.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp); info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
info.spdif_out = (unsigned char)hdsp_spdif_out(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.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(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) if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp); 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; hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
break; 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: default:
/* should never get here */ /* should never get here */
break; break;
@ -4930,6 +5349,9 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
/* caution: max length of firmware filename is 30! */ /* caution: max length of firmware filename is 30! */
switch (hdsp->io_type) { switch (hdsp->io_type) {
case RPM:
fwfile = "rpm_firmware.bin";
break;
case Multiface: case Multiface:
if (hdsp->firmware_rev == 0xa) if (hdsp->firmware_rev == 0xa)
fwfile = "multiface_firmware.bin"; fwfile = "multiface_firmware.bin";
@ -5100,7 +5522,9 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
return 0; return 0;
} else { } else {
snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n"); 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; hdsp->io_type = Multiface;
else else
hdsp->io_type = Digiface; 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 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; return snd_ctl_enum_info(info, 1, 3, texts);
info->count = 1;
info->value.enumerated.items = 3;
if (info->value.enumerated.item > 2)
info->value.enumerated.item = 2;
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
return 0;
} }
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) 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_data *priv = snd_soc_codec_get_drvdata(codec);
struct wm8350 *wm8350 = dev_get_platdata(codec->dev); struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
int ret;
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA); 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->hpr.jack = NULL;
priv->mic.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 /* if there was any work waiting then we run it now and
* wait for its completion */ * wait for its completion */
if (ret) { flush_delayed_work_sync(&codec->delayed_work);
schedule_delayed_work(&codec->delayed_work, 0);
flush_scheduled_work();
}
wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); 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; 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) static int wm8753_probe(struct snd_soc_codec *codec)
{ {
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(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 */ /* power down chip */
static int wm8753_remove(struct snd_soc_codec *codec) 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); wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0; return 0;

View File

@ -67,25 +67,6 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0); module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); 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 */ /* codec register dump */
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) 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 */ /* close any waiting streams and save state */
for (i = 0; i < card->num_rtd; i++) { 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; 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 */ /* make sure any delayed work runs */
for (i = 0; i < card->num_rtd; i++) { for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->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 */ /* 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. */ * now, we're shutting down so no imminent restart. */
for (i = 0; i < card->num_rtd; i++) { for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->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); 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; format = 1 << UAC_FORMAT_TYPE_I_PCM;
} }
if (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", snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
chip->dev->devnum, fp->iface, fp->altsetting, chip->dev->devnum, fp->iface, fp->altsetting,
sample_width, sample_bytes); sample_width, sample_bytes);

View File

@ -850,8 +850,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
return; return;
} }
memset(urb->transfer_buffer + count, 0xFD, 9 - count); memset(urb->transfer_buffer + count, 0xFD, ep->max_transfer - count);
urb->transfer_buffer_length = count; urb->transfer_buffer_length = ep->max_transfer;
} }
static struct usb_protocol_ops snd_usbmidi_122l_ops = { 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" */ case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */
ep->max_transfer = 4; ep->max_transfer = 4;
break; 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) { for (i = 0; i < OUTPUT_URBS; ++i) {
buffer = usb_alloc_coherent(umidi->dev, 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" }; static const char *const names[] = { "High Load", "Light Load" };
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(info, 1, 2, names);
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;
} }
static int roland_load_get(struct snd_kcontrol *kcontrol, 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) 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; 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)) if (snd_BUG_ON(!itemlist))
return -EINVAL; return -EINVAL;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; return snd_ctl_enum_info(uinfo, 1, cval->max, itemlist);
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;
} }
/* get callback for selector unit */ /* get callback for selector unit */

View File

@ -705,11 +705,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.data = (const struct snd_usb_audio_quirk[]) { .data = (const struct snd_usb_audio_quirk[]) {
{ {
.ifnum = 0, .ifnum = 0,
.type = QUIRK_IGNORE_INTERFACE .type = QUIRK_AUDIO_STANDARD_INTERFACE
}, },
{ {
.ifnum = 1, .ifnum = 1,
.type = QUIRK_IGNORE_INTERFACE .type = QUIRK_AUDIO_STANDARD_INTERFACE
}, },
{ {
.ifnum = 2, .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 file *file, poll_table *wait)
{ {
struct us122l *us122l = hw->private_data; struct us122l *us122l = hw->private_data;
struct usb_stream *s = us122l->sk.s;
unsigned *polled; unsigned *polled;
unsigned int mask; unsigned int mask;
poll_wait(file, &us122l->sk.sleep, wait); poll_wait(file, &us122l->sk.sleep, wait);
switch (s->state) { mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
case usb_stream_ready: if (mutex_trylock(&us122l->mutex)) {
if (us122l->first == file) struct usb_stream *s = us122l->sk.s;
polled = &s->periods_polled; if (s && s->state == usb_stream_ready) {
else if (us122l->first == file)
polled = &us122l->second_periods_polled; polled = &s->periods_polled;
if (*polled != s->periods_done) { else
*polled = s->periods_done; polled = &us122l->second_periods_polled;
mask = POLLIN | POLLOUT | POLLWRNORM; if (*polled != s->periods_done) {
break; *polled = s->periods_done;
mask = POLLIN | POLLOUT | POLLWRNORM;
} else
mask = 0;
} }
/* Fall through */ mutex_unlock(&us122l->mutex);
mask = 0;
break;
default:
mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
break;
} }
return mask; 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 usb_stream_config *cfg;
struct us122l *us122l = hw->private_data; struct us122l *us122l = hw->private_data;
struct usb_stream *s;
unsigned min_period_frames; unsigned min_period_frames;
int err = 0; int err = 0;
bool high_speed; 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); snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
mutex_lock(&us122l->mutex); mutex_lock(&us122l->mutex);
s = us122l->sk.s;
if (!us122l->master) if (!us122l->master)
us122l->master = file; us122l->master = file;
else if (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; err = -EIO;
goto unlock; goto unlock;
} }
us122l->slave = file; us122l->slave = file;
} }
if (!us122l->sk.s || if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg)) ||
memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) || s->state == usb_stream_xrun) {
us122l->sk.s->state == usb_stream_xrun) {
us122l_stop(us122l); us122l_stop(us122l);
if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames)) if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
err = -EIO; err = -EIO;
@ -448,6 +446,7 @@ unlock:
mutex_unlock(&us122l->mutex); mutex_unlock(&us122l->mutex);
free: free:
kfree(cfg); kfree(cfg);
wake_up_all(&us122l->sk.sleep);
return err; return err;
} }