Merge branch 'topic/misc' into for-linus
This commit is contained in:
Коммит
e38302f782
|
@ -974,13 +974,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
See hdspm.txt for details.
|
||||
|
||||
Module snd-hifier
|
||||
-----------------
|
||||
|
||||
Module for the MediaTek/TempoTec HiFier Fantasia sound card.
|
||||
|
||||
This module supports autoprobe and multiple cards.
|
||||
|
||||
Module snd-ice1712
|
||||
------------------
|
||||
|
||||
|
@ -1531,15 +1524,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
Module snd-oxygen
|
||||
-----------------
|
||||
|
||||
Module for sound cards based on the C-Media CMI8788 chip:
|
||||
Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
|
||||
* Asound A-8788
|
||||
* Asus Xonar DG
|
||||
* AuzenTech X-Meridian
|
||||
* AuzenTech X-Meridian 2G
|
||||
* Bgears b-Enspirer
|
||||
* Club3D Theatron DTS
|
||||
* HT-Omega Claro (plus)
|
||||
* HT-Omega Claro halo (XT)
|
||||
* Kuroutoshikou CMI8787-HG2PCI
|
||||
* Razer Barracuda AC-1
|
||||
* Sondigo Inferno
|
||||
* TempoTec HiFier Fantasia
|
||||
* TempoTec HiFier Serenade
|
||||
|
||||
This module supports autoprobe and multiple cards.
|
||||
|
||||
|
@ -2006,9 +2004,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
Module snd-virtuoso
|
||||
-------------------
|
||||
|
||||
Module for sound cards based on the Asus AV100/AV200 chips,
|
||||
i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST
|
||||
(Deluxe) and Essence STX.
|
||||
Module for sound cards based on the Asus AV66/AV100/AV200 chips,
|
||||
i.e., Xonar D1, DX, D2, D2X, DS, Essence ST (Deluxe), Essence STX,
|
||||
HDAV1.3 (Deluxe), and HDAV1.3 Slim.
|
||||
|
||||
This module supports autoprobe and multiple cards.
|
||||
|
||||
|
|
43
MAINTAINERS
43
MAINTAINERS
|
@ -1434,6 +1434,14 @@ S: Supported
|
|||
F: block/bsg.c
|
||||
F: include/linux/bsg.h
|
||||
|
||||
BT87X AUDIO DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: Documentation/sound/alsa/Bt87x.txt
|
||||
F: sound/pci/bt87x.c
|
||||
|
||||
BT8XXGPIO DRIVER
|
||||
M: Michael Buesch <mb@bu3sch.de>
|
||||
W: http://bu3sch.de/btgpio.php
|
||||
|
@ -1459,6 +1467,13 @@ S: Maintained
|
|||
F: Documentation/video4linux/bttv/
|
||||
F: drivers/media/video/bt8xx/bttv*
|
||||
|
||||
C-MEDIA CMI8788 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: sound/pci/oxygen/
|
||||
|
||||
CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
|
||||
M: David Howells <dhowells@redhat.com>
|
||||
L: linux-cachefs@redhat.com
|
||||
|
@ -2249,6 +2264,13 @@ W: bluesmoke.sourceforge.net
|
|||
S: Maintained
|
||||
F: drivers/edac/r82600_edac.c
|
||||
|
||||
EDIROL UA-101/UA-1000 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: sound/usb/misc/ua101.c
|
||||
|
||||
EEEPC LAPTOP EXTRAS DRIVER
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
|
@ -3393,6 +3415,13 @@ L: linux-serial@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/serial/jsm/
|
||||
|
||||
K10TEMP HARDWARE MONITORING DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/k10temp
|
||||
F: drivers/hwmon/k10temp.c
|
||||
|
||||
K8TEMP HARDWARE MONITORING DRIVER
|
||||
M: Rudolf Marek <r.marek@assembler.cz>
|
||||
L: lm-sensors@lm-sensors.org
|
||||
|
@ -4409,6 +4438,13 @@ F: drivers/of
|
|||
F: include/linux/of*.h
|
||||
K: of_get_property
|
||||
|
||||
OPL4 DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: sound/drivers/opl4/
|
||||
|
||||
OPROFILE
|
||||
M: Robert Richter <robert.richter@amd.com>
|
||||
L: oprofile-list@lists.sf.net
|
||||
|
@ -6141,6 +6177,13 @@ S: Maintained
|
|||
W: http://www.one-eyed-alien.net/~mdharm/linux-usb/
|
||||
F: drivers/usb/storage/
|
||||
|
||||
USB MIDI DRIVER
|
||||
M: Clemens Ladisch <clemens@ladisch.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: sound/usb/midi.*
|
||||
|
||||
USB OHCI DRIVER
|
||||
M: David Brownell <dbrownell@users.sourceforge.net>
|
||||
L: linux-usb@vger.kernel.org
|
||||
|
|
|
@ -259,6 +259,7 @@ typedef int __bitwise snd_pcm_subformat_t;
|
|||
#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */
|
||||
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
|
||||
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
|
||||
#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */
|
||||
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
|
||||
|
||||
typedef int __bitwise snd_pcm_state_t;
|
||||
|
@ -334,6 +335,8 @@ typedef int snd_pcm_hw_param_t;
|
|||
#define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME
|
||||
|
||||
#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */
|
||||
#define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */
|
||||
#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */
|
||||
|
||||
struct snd_interval {
|
||||
unsigned int min, max;
|
||||
|
|
|
@ -160,12 +160,14 @@ static inline struct snd_ctl_elem_id *snd_ctl_build_ioff(struct snd_ctl_elem_id
|
|||
}
|
||||
|
||||
/*
|
||||
* Frequently used control callbacks
|
||||
* Frequently used control callbacks/helpers
|
||||
*/
|
||||
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
|
||||
unsigned int items, const char *const names[]);
|
||||
|
||||
/*
|
||||
* virtual master control
|
||||
|
|
|
@ -28,6 +28,7 @@ enum HDSP_IO_Type {
|
|||
Multiface,
|
||||
H9652,
|
||||
H9632,
|
||||
RPM,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
/* these minors can still be used for autoloading devices (/dev/aload*) */
|
||||
#define SNDRV_MINOR_CONTROL 0 /* 0 */
|
||||
#define SNDRV_MINOR_GLOBAL 1 /* 1 */
|
||||
#define SNDRV_MINOR_SEQUENCER (SNDRV_MINOR_GLOBAL + 0 * 32)
|
||||
#define SNDRV_MINOR_TIMER (SNDRV_MINOR_GLOBAL + 1 * 32)
|
||||
#define SNDRV_MINOR_SEQUENCER 1 /* SNDRV_MINOR_GLOBAL + 0 * 32 */
|
||||
#define SNDRV_MINOR_TIMER 33 /* SNDRV_MINOR_GLOBAL + 1 * 32 */
|
||||
|
||||
#ifndef CONFIG_SND_DYNAMIC_MINORS
|
||||
/* 2 - 3 (reserved) */
|
||||
|
|
|
@ -297,6 +297,7 @@ struct snd_pcm_runtime {
|
|||
unsigned int info;
|
||||
unsigned int rate_num;
|
||||
unsigned int rate_den;
|
||||
unsigned int no_period_wakeup: 1;
|
||||
|
||||
/* -- SW params -- */
|
||||
int tstamp_mode; /* mmap timestamp is updated */
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
/*
|
||||
* Let drivers decide whether they want to support given codec from their
|
||||
* probe method. Drivers have direct access to the struct snd_ac97 structure and may
|
||||
* decide based on the id field amongst other things.
|
||||
* probe method. Drivers have direct access to the struct snd_ac97
|
||||
* structure and may decide based on the id field amongst other things.
|
||||
*/
|
||||
static int ac97_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
|
|
|
@ -1114,7 +1114,6 @@ static int onyx_i2c_remove(struct i2c_client *client)
|
|||
of_node_put(onyx->codec.node);
|
||||
if (onyx->codec_info)
|
||||
kfree(onyx->codec_info);
|
||||
i2c_set_clientdata(client, onyx);
|
||||
kfree(onyx);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -287,10 +287,9 @@ static void ftr_gpio_exit(struct gpio_runtime *rt)
|
|||
free_irq(linein_detect_irq, &rt->line_in_notify);
|
||||
if (rt->line_out_notify.gpio_private)
|
||||
free_irq(lineout_detect_irq, &rt->line_out_notify);
|
||||
cancel_delayed_work(&rt->headphone_notify.work);
|
||||
cancel_delayed_work(&rt->line_in_notify.work);
|
||||
cancel_delayed_work(&rt->line_out_notify.work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&rt->headphone_notify.work);
|
||||
cancel_delayed_work_sync(&rt->line_in_notify.work);
|
||||
cancel_delayed_work_sync(&rt->line_out_notify.work);
|
||||
mutex_destroy(&rt->headphone_notify.mutex);
|
||||
mutex_destroy(&rt->line_in_notify.mutex);
|
||||
mutex_destroy(&rt->line_out_notify.mutex);
|
||||
|
|
|
@ -107,10 +107,9 @@ static void pmf_gpio_exit(struct gpio_runtime *rt)
|
|||
|
||||
/* make sure no work is pending before freeing
|
||||
* all things */
|
||||
cancel_delayed_work(&rt->headphone_notify.work);
|
||||
cancel_delayed_work(&rt->line_in_notify.work);
|
||||
cancel_delayed_work(&rt->line_out_notify.work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&rt->headphone_notify.work);
|
||||
cancel_delayed_work_sync(&rt->line_in_notify.work);
|
||||
cancel_delayed_work_sync(&rt->line_out_notify.work);
|
||||
|
||||
mutex_destroy(&rt->headphone_notify.mutex);
|
||||
mutex_destroy(&rt->line_in_notify.mutex);
|
||||
|
|
|
@ -1488,7 +1488,7 @@ int snd_ctl_create(struct snd_card *card)
|
|||
}
|
||||
|
||||
/*
|
||||
* Frequently used control callbacks
|
||||
* Frequently used control callbacks/helpers
|
||||
*/
|
||||
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
|
@ -1513,3 +1513,29 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
|
|||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
|
||||
|
||||
/**
|
||||
* snd_ctl_enum_info - fills the info structure for an enumerated control
|
||||
* @info: the structure to be filled
|
||||
* @channels: the number of the control's channels; often one
|
||||
* @items: the number of control values; also the size of @names
|
||||
* @names: an array containing the names of all control values
|
||||
*
|
||||
* Sets all required fields in @info to their appropriate values.
|
||||
* If the control's accessibility is not the default (readable and writable),
|
||||
* the caller has to fill @info->access.
|
||||
*/
|
||||
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
|
||||
unsigned int items, const char *const names[])
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = channels;
|
||||
info->value.enumerated.items = items;
|
||||
if (info->value.enumerated.item >= items)
|
||||
info->value.enumerated.item = items - 1;
|
||||
strlcpy(info->value.enumerated.name,
|
||||
names[info->value.enumerated.item],
|
||||
sizeof(info->value.enumerated.name));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_ctl_enum_info);
|
||||
|
|
|
@ -453,8 +453,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
|
|||
} else {
|
||||
*params = *save;
|
||||
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
|
||||
if (max < 0)
|
||||
if (max < 0) {
|
||||
kfree(save);
|
||||
return max;
|
||||
}
|
||||
last = 1;
|
||||
}
|
||||
_end:
|
||||
|
|
|
@ -373,6 +373,27 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
(unsigned long)new_hw_ptr,
|
||||
(unsigned long)runtime->hw_ptr_base);
|
||||
}
|
||||
|
||||
if (runtime->no_period_wakeup) {
|
||||
/*
|
||||
* Without regular period interrupts, we have to check
|
||||
* the elapsed time to detect xruns.
|
||||
*/
|
||||
jdelta = jiffies - runtime->hw_ptr_jiffies;
|
||||
if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
|
||||
goto no_delta_check;
|
||||
hdelta = jdelta - delta * HZ / runtime->rate;
|
||||
while (hdelta > runtime->hw_ptr_buffer_jiffies / 2 + 1) {
|
||||
delta += runtime->buffer_size;
|
||||
hw_base += runtime->buffer_size;
|
||||
if (hw_base >= runtime->boundary)
|
||||
hw_base = 0;
|
||||
new_hw_ptr = hw_base + pos;
|
||||
hdelta -= runtime->hw_ptr_buffer_jiffies;
|
||||
}
|
||||
goto no_delta_check;
|
||||
}
|
||||
|
||||
/* something must be really wrong */
|
||||
if (delta >= runtime->buffer_size + runtime->period_size) {
|
||||
hw_ptr_error(substream,
|
||||
|
@ -442,6 +463,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
(long)old_hw_ptr);
|
||||
}
|
||||
|
||||
no_delta_check:
|
||||
if (runtime->status->hw_ptr == new_hw_ptr)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -422,6 +422,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
runtime->info = params->info;
|
||||
runtime->rate_num = params->rate_num;
|
||||
runtime->rate_den = params->rate_den;
|
||||
runtime->no_period_wakeup =
|
||||
(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
|
||||
(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
|
||||
|
||||
bits = snd_pcm_format_physical_width(runtime->format);
|
||||
runtime->sample_bits = bits;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "seq_timer.h"
|
||||
#include "seq_system.h"
|
||||
#include "seq_info.h"
|
||||
#include <sound/minors.h>
|
||||
#include <sound/seq_device.h>
|
||||
|
||||
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
|
||||
|
@ -73,6 +74,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe
|
|||
module_param(seq_default_timer_resolution, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
|
||||
|
||||
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER);
|
||||
MODULE_ALIAS("devname:snd/seq");
|
||||
|
||||
/*
|
||||
* INIT PART
|
||||
*/
|
||||
|
|
|
@ -188,14 +188,22 @@ static const struct file_operations snd_fops =
|
|||
};
|
||||
|
||||
#ifdef CONFIG_SND_DYNAMIC_MINORS
|
||||
static int snd_find_free_minor(void)
|
||||
static int snd_find_free_minor(int type)
|
||||
{
|
||||
int minor;
|
||||
|
||||
/* static minors for module auto loading */
|
||||
if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
|
||||
return SNDRV_MINOR_SEQUENCER;
|
||||
if (type == SNDRV_DEVICE_TYPE_TIMER)
|
||||
return SNDRV_MINOR_TIMER;
|
||||
|
||||
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
|
||||
/* skip minors still used statically for autoloading devices */
|
||||
if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
|
||||
minor == SNDRV_MINOR_SEQUENCER)
|
||||
/* skip static minors still used for module auto loading */
|
||||
if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
|
||||
continue;
|
||||
if (minor == SNDRV_MINOR_SEQUENCER ||
|
||||
minor == SNDRV_MINOR_TIMER)
|
||||
continue;
|
||||
if (!snd_minors[minor])
|
||||
return minor;
|
||||
|
@ -269,7 +277,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
|
|||
preg->private_data = private_data;
|
||||
mutex_lock(&sound_mutex);
|
||||
#ifdef CONFIG_SND_DYNAMIC_MINORS
|
||||
minor = snd_find_free_minor();
|
||||
minor = snd_find_free_minor(type);
|
||||
#else
|
||||
minor = snd_kernel_minor(type, card, dev);
|
||||
if (minor >= 0 && snd_minors[minor])
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
#include <sound/initval.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
|
||||
#define DEFAULT_TIMER_LIMIT 3
|
||||
#if defined(CONFIG_SND_HRTIMER) || defined(CONFIG_SND_HRTIMER_MODULE)
|
||||
#define DEFAULT_TIMER_LIMIT 4
|
||||
#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
|
||||
#define DEFAULT_TIMER_LIMIT 2
|
||||
#else
|
||||
|
@ -52,6 +52,9 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
|
|||
module_param(timer_tstamp_monotonic, int, 0444);
|
||||
MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
|
||||
|
||||
MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
|
||||
MODULE_ALIAS("devname:snd/timer");
|
||||
|
||||
struct snd_timer_user {
|
||||
struct snd_timer_instance *timeri;
|
||||
int tread; /* enhanced read with timestamps and events */
|
||||
|
|
|
@ -1143,8 +1143,8 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
|
|||
(resource->start) + 1);
|
||||
if (ml403_ac97cr->port == NULL) {
|
||||
snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
|
||||
"unable to remap memory region (%x to %x)\n",
|
||||
resource->start, resource->end);
|
||||
"unable to remap memory region (%pR)\n",
|
||||
resource);
|
||||
snd_ml403_ac97cr_free(ml403_ac97cr);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
|
|
@ -57,8 +57,7 @@ static void snd_ak4113_free(struct ak4113 *chip)
|
|||
{
|
||||
chip->init = 1; /* don't schedule new work */
|
||||
mb();
|
||||
cancel_delayed_work(&chip->work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
|
@ -141,7 +140,7 @@ void snd_ak4113_reinit(struct ak4113 *chip)
|
|||
{
|
||||
chip->init = 1;
|
||||
mb();
|
||||
flush_scheduled_work();
|
||||
flush_delayed_work_sync(&chip->work);
|
||||
ak4113_init_regs(chip);
|
||||
/* bring up statistics / event queing */
|
||||
chip->init = 0;
|
||||
|
|
|
@ -67,8 +67,7 @@ static void snd_ak4114_free(struct ak4114 *chip)
|
|||
{
|
||||
chip->init = 1; /* don't schedule new work */
|
||||
mb();
|
||||
cancel_delayed_work(&chip->work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
|
@ -154,7 +153,7 @@ void snd_ak4114_reinit(struct ak4114 *chip)
|
|||
{
|
||||
chip->init = 1;
|
||||
mb();
|
||||
flush_scheduled_work();
|
||||
flush_delayed_work_sync(&chip->work);
|
||||
ak4114_init_regs(chip);
|
||||
/* bring up statistics / event queing */
|
||||
chip->init = 0;
|
||||
|
|
|
@ -209,7 +209,7 @@ config SND_OXYGEN_LIB
|
|||
tristate
|
||||
|
||||
config SND_OXYGEN
|
||||
tristate "C-Media 8788 (Oxygen)"
|
||||
tristate "C-Media 8786, 8787, 8788 (Oxygen)"
|
||||
select SND_OXYGEN_LIB
|
||||
select SND_PCM
|
||||
select SND_MPU401_UART
|
||||
|
@ -217,13 +217,18 @@ config SND_OXYGEN
|
|||
Say Y here to include support for sound cards based on the
|
||||
C-Media CMI8788 (Oxygen HD Audio) chip:
|
||||
* Asound A-8788
|
||||
* Asus Xonar DG
|
||||
* AuzenTech X-Meridian
|
||||
* AuzenTech X-Meridian 2G
|
||||
* Bgears b-Enspirer
|
||||
* Club3D Theatron DTS
|
||||
* HT-Omega Claro (plus)
|
||||
* HT-Omega Claro halo (XT)
|
||||
* Kuroutoshikou CMI8787-HG2PCI
|
||||
* Razer Barracuda AC-1
|
||||
* Sondigo Inferno
|
||||
* TempoTec/MediaTek HiFier Fantasia
|
||||
* TempoTec/MediaTek HiFier Serenade
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-oxygen.
|
||||
|
@ -578,18 +583,6 @@ config SND_HDSPM
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hdspm.
|
||||
|
||||
config SND_HIFIER
|
||||
tristate "TempoTec HiFier Fantasia"
|
||||
select SND_OXYGEN_LIB
|
||||
select SND_PCM
|
||||
select SND_MPU401_UART
|
||||
help
|
||||
Say Y here to include support for the MediaTek/TempoTec HiFier
|
||||
Fantasia sound card.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hifier.
|
||||
|
||||
config SND_ICE1712
|
||||
tristate "ICEnsemble ICE1712 (Envy24)"
|
||||
select SND_MPU401_UART
|
||||
|
@ -826,8 +819,8 @@ config SND_VIRTUOSO
|
|||
Say Y here to include support for sound cards based on the
|
||||
Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
|
||||
Essence ST (Deluxe), and Essence STX.
|
||||
Support for the HDAV1.3 (Deluxe) is incomplete; for the
|
||||
HDAV1.3 Slim and Xense, missing.
|
||||
Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
|
||||
for the Xense, missing.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-virtuoso.
|
||||
|
|
|
@ -1014,8 +1014,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
|
|||
{
|
||||
if (ac97) {
|
||||
#ifdef CONFIG_SND_AC97_POWER_SAVE
|
||||
cancel_delayed_work(&ac97->power_work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&ac97->power_work);
|
||||
#endif
|
||||
snd_ac97_proc_done(ac97);
|
||||
if (ac97->bus)
|
||||
|
@ -2456,8 +2455,7 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
|
|||
if (ac97->build_ops->suspend)
|
||||
ac97->build_ops->suspend(ac97);
|
||||
#ifdef CONFIG_SND_AC97_POWER_SAVE
|
||||
cancel_delayed_work(&ac97->power_work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&ac97->power_work);
|
||||
#endif
|
||||
snd_ac97_powerdown(ac97);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
|
||||
* Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
|
||||
* Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de>
|
||||
*
|
||||
* Framework borrowed from Bart Hartgers's als4000.c.
|
||||
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
|
||||
|
@ -175,6 +175,7 @@
|
|||
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bug.h> /* WARN_ONCE */
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -201,14 +202,15 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
|
|||
|
||||
/* === Debug settings ===
|
||||
Further diagnostic functionality than the settings below
|
||||
does not need to be provided, since one can easily write a bash script
|
||||
does not need to be provided, since one can easily write a POSIX shell script
|
||||
to dump the card's I/O ports (those listed in lspci -v -v):
|
||||
function dump()
|
||||
dump()
|
||||
{
|
||||
local descr=$1; local addr=$2; local count=$3
|
||||
|
||||
echo "${descr}: ${count} @ ${addr}:"
|
||||
dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
|
||||
dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
|
||||
2>/dev/null| hexdump -C
|
||||
}
|
||||
and then use something like
|
||||
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
|
||||
|
@ -216,14 +218,14 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
|
|||
possibly within a "while true; do ... sleep 1; done" loop.
|
||||
Tweaking ports could be done using
|
||||
VALSTRING="`printf "%02x" $value`"
|
||||
printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
|
||||
printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
|
||||
2>/dev/null
|
||||
*/
|
||||
|
||||
#define DEBUG_MISC 0
|
||||
#define DEBUG_CALLS 0
|
||||
#define DEBUG_MIXER 0
|
||||
#define DEBUG_CODEC 0
|
||||
#define DEBUG_IO 0
|
||||
#define DEBUG_TIMER 0
|
||||
#define DEBUG_GAME 0
|
||||
#define DEBUG_PM 0
|
||||
|
@ -291,19 +293,23 @@ static int seqtimer_scaling = 128;
|
|||
module_param(seqtimer_scaling, int, 0444);
|
||||
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
|
||||
|
||||
struct snd_azf3328_codec_data {
|
||||
unsigned long io_base;
|
||||
struct snd_pcm_substream *substream;
|
||||
bool running;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
enum snd_azf3328_codec_type {
|
||||
/* warning: fixed indices (also used for bitmask checks!) */
|
||||
AZF_CODEC_PLAYBACK = 0,
|
||||
AZF_CODEC_CAPTURE = 1,
|
||||
AZF_CODEC_I2S_OUT = 2,
|
||||
};
|
||||
|
||||
struct snd_azf3328_codec_data {
|
||||
unsigned long io_base; /* keep first! (avoid offset calc) */
|
||||
unsigned int dma_base; /* helper to avoid an indirection in hotpath */
|
||||
spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
|
||||
struct snd_pcm_substream *substream;
|
||||
bool running;
|
||||
enum snd_azf3328_codec_type type;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct snd_azf3328 {
|
||||
/* often-used fields towards beginning, then grouped */
|
||||
|
||||
|
@ -362,6 +368,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
|
|||
static int
|
||||
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
|
||||
{
|
||||
/* Well, strictly spoken, the inb/outb sequence isn't atomic
|
||||
and would need locking. However we currently don't care
|
||||
since it potentially complicates matters. */
|
||||
u8 prev = inb(reg), new;
|
||||
|
||||
new = (do_set) ? (prev|mask) : (prev & ~mask);
|
||||
|
@ -413,6 +422,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
|
|||
outl(value, codec->io_base + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
|
||||
unsigned reg, const void *buffer, int count
|
||||
)
|
||||
{
|
||||
unsigned long addr = codec->io_base + reg;
|
||||
if (count) {
|
||||
const u32 *buf = buffer;
|
||||
do {
|
||||
outl(*buf++, addr);
|
||||
addr += 4;
|
||||
} while (--count);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32
|
||||
snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
|
||||
{
|
||||
|
@ -943,38 +967,43 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static void
|
||||
snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
|
||||
enum snd_azf3328_codec_type codec_type,
|
||||
snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
|
||||
enum azf_freq_t bitrate,
|
||||
unsigned int format_width,
|
||||
unsigned int channels
|
||||
)
|
||||
{
|
||||
unsigned long flags;
|
||||
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
|
||||
u16 val = 0xff00;
|
||||
u8 freq = 0;
|
||||
|
||||
snd_azf3328_dbgcallenter();
|
||||
switch (bitrate) {
|
||||
case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
|
||||
case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
|
||||
case AZF_FREQ_5512:
|
||||
/* the AZF3328 names it "5510" for some strange reason */
|
||||
val |= SOUNDFORMAT_FREQ_5510; break;
|
||||
case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break;
|
||||
case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break;
|
||||
case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break;
|
||||
case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
|
||||
case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
|
||||
case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
|
||||
case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
|
||||
case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
|
||||
#define AZF_FMT_XLATE(in_freq, out_bits) \
|
||||
do { \
|
||||
case AZF_FREQ_ ## in_freq: \
|
||||
freq = SOUNDFORMAT_FREQ_ ## out_bits; \
|
||||
break; \
|
||||
} while (0);
|
||||
AZF_FMT_XLATE(4000, SUSPECTED_4000)
|
||||
AZF_FMT_XLATE(4800, SUSPECTED_4800)
|
||||
/* the AZF3328 names it "5510" for some strange reason: */
|
||||
AZF_FMT_XLATE(5512, 5510)
|
||||
AZF_FMT_XLATE(6620, 6620)
|
||||
AZF_FMT_XLATE(8000, 8000)
|
||||
AZF_FMT_XLATE(9600, 9600)
|
||||
AZF_FMT_XLATE(11025, 11025)
|
||||
AZF_FMT_XLATE(13240, SUSPECTED_13240)
|
||||
AZF_FMT_XLATE(16000, 16000)
|
||||
AZF_FMT_XLATE(22050, 22050)
|
||||
AZF_FMT_XLATE(32000, 32000)
|
||||
default:
|
||||
snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
|
||||
/* fall-through */
|
||||
case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
|
||||
case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
|
||||
case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
|
||||
AZF_FMT_XLATE(44100, 44100)
|
||||
AZF_FMT_XLATE(48000, 48000)
|
||||
AZF_FMT_XLATE(66200, SUSPECTED_66200)
|
||||
#undef AZF_FMT_XLATE
|
||||
}
|
||||
/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
|
||||
/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
|
||||
|
@ -986,13 +1015,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
|
|||
/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
|
||||
/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
|
||||
|
||||
val |= freq;
|
||||
|
||||
if (channels == 2)
|
||||
val |= SOUNDFORMAT_FLAG_2CHANNELS;
|
||||
|
||||
if (format_width == 16)
|
||||
val |= SOUNDFORMAT_FLAG_16BIT;
|
||||
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
spin_lock_irqsave(codec->lock, flags);
|
||||
|
||||
/* set bitrate/format */
|
||||
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
|
||||
|
@ -1004,7 +1035,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
|
|||
* (FIXME: yes, it works, but what exactly am I doing here?? :)
|
||||
* FIXME: does this have some side effects for full-duplex
|
||||
* or other dramatic side effects? */
|
||||
if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
|
||||
/* do it for non-capture codecs only */
|
||||
if (codec->type != AZF_CODEC_CAPTURE)
|
||||
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
|
||||
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
|
||||
DMA_RUN_SOMETHING1 |
|
||||
|
@ -1014,20 +1046,19 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
|
|||
DMA_SOMETHING_ELSE
|
||||
);
|
||||
|
||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
spin_unlock_irqrestore(codec->lock, flags);
|
||||
snd_azf3328_dbgcallleave();
|
||||
}
|
||||
|
||||
static inline void
|
||||
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
|
||||
enum snd_azf3328_codec_type codec_type
|
||||
snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
|
||||
)
|
||||
{
|
||||
/* choose lowest frequency for low power consumption.
|
||||
* While this will cause louder noise due to rather coarse frequency,
|
||||
* it should never matter since output should always
|
||||
* get disabled properly when idle anyway. */
|
||||
snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
|
||||
snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1101,69 +1132,87 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
|
|||
/* ...and adjust clock, too
|
||||
* (reduce noise and power consumption) */
|
||||
if (!enable)
|
||||
snd_azf3328_codec_setfmt_lowpower(
|
||||
chip,
|
||||
codec_type
|
||||
);
|
||||
snd_azf3328_codec_setfmt_lowpower(codec);
|
||||
codec->running = enable;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
|
||||
enum snd_azf3328_codec_type codec_type,
|
||||
snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec,
|
||||
unsigned long addr,
|
||||
unsigned int count,
|
||||
unsigned int size
|
||||
unsigned int period_bytes,
|
||||
unsigned int buffer_bytes
|
||||
)
|
||||
{
|
||||
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
|
||||
snd_azf3328_dbgcallenter();
|
||||
WARN_ONCE(period_bytes & 1, "odd period length!?\n");
|
||||
WARN_ONCE(buffer_bytes != 2 * period_bytes,
|
||||
"missed our input expectations! %u vs. %u\n",
|
||||
buffer_bytes, period_bytes);
|
||||
if (!codec->running) {
|
||||
/* AZF3328 uses a two buffer pointer DMA transfer approach */
|
||||
|
||||
unsigned long flags, addr_area2;
|
||||
unsigned long flags;
|
||||
|
||||
/* width 32bit (prevent overflow): */
|
||||
u32 count_areas, lengths;
|
||||
u32 area_length;
|
||||
struct codec_setup_io {
|
||||
u32 dma_start_1;
|
||||
u32 dma_start_2;
|
||||
u32 dma_lengths;
|
||||
} __attribute__((packed)) setup_io;
|
||||
|
||||
count_areas = size/2;
|
||||
addr_area2 = addr+count_areas;
|
||||
snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
|
||||
addr, count_areas, addr_area2, count_areas);
|
||||
area_length = buffer_bytes/2;
|
||||
|
||||
count_areas--; /* max. index */
|
||||
setup_io.dma_start_1 = addr;
|
||||
setup_io.dma_start_2 = addr+area_length;
|
||||
|
||||
snd_azf3328_dbgcodec(
|
||||
"setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
|
||||
setup_io.dma_start_1, area_length,
|
||||
setup_io.dma_start_2, area_length,
|
||||
period_bytes, buffer_bytes);
|
||||
|
||||
/* Hmm, are we really supposed to decrement this by 1??
|
||||
Most definitely certainly not: configuring full length does
|
||||
work properly (i.e. likely better), and BTW we
|
||||
violated possibly differing frame sizes with this...
|
||||
|
||||
area_length--; |* max. index *|
|
||||
*/
|
||||
|
||||
/* build combined I/O buffer length word */
|
||||
lengths = (count_areas << 16) | (count_areas);
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
|
||||
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
|
||||
addr_area2);
|
||||
snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
|
||||
lengths);
|
||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
setup_io.dma_lengths = (area_length << 16) | (area_length);
|
||||
|
||||
spin_lock_irqsave(codec->lock, flags);
|
||||
snd_azf3328_codec_outl_multi(
|
||||
codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
|
||||
);
|
||||
spin_unlock_irqrestore(codec->lock, flags);
|
||||
}
|
||||
snd_azf3328_dbgcallleave();
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
|
||||
snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
#if 0
|
||||
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_azf3328_codec_data *codec = runtime->private_data;
|
||||
#if 0
|
||||
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
|
||||
unsigned int count = snd_pcm_lib_period_bytes(substream);
|
||||
#endif
|
||||
|
||||
snd_azf3328_dbgcallenter();
|
||||
|
||||
codec->dma_base = runtime->dma_addr;
|
||||
|
||||
#if 0
|
||||
snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
|
||||
snd_azf3328_codec_setfmt(codec,
|
||||
runtime->rate,
|
||||
snd_pcm_format_width(runtime->format),
|
||||
runtime->channels);
|
||||
snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
|
||||
snd_azf3328_codec_setdmaa(codec,
|
||||
runtime->dma_addr, count, size);
|
||||
#endif
|
||||
snd_azf3328_dbgcallleave();
|
||||
|
@ -1171,24 +1220,23 @@ snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
|
||||
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_azf3328_codec_data *codec = runtime->private_data;
|
||||
int result = 0;
|
||||
u16 flags1;
|
||||
bool previously_muted = 0;
|
||||
bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
|
||||
bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
|
||||
|
||||
snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
|
||||
snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_azf3328_dbgcodec("START %s\n", codec->name);
|
||||
|
||||
if (is_playback_codec) {
|
||||
if (is_main_mixer_playback_codec) {
|
||||
/* mute WaveOut (avoid clicking during setup) */
|
||||
previously_muted =
|
||||
snd_azf3328_mixer_set_mute(
|
||||
|
@ -1196,12 +1244,12 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
);
|
||||
}
|
||||
|
||||
snd_azf3328_codec_setfmt(chip, codec_type,
|
||||
snd_azf3328_codec_setfmt(codec,
|
||||
runtime->rate,
|
||||
snd_pcm_format_width(runtime->format),
|
||||
runtime->channels);
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
spin_lock(codec->lock);
|
||||
/* first, remember current value: */
|
||||
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
|
||||
|
||||
|
@ -1211,14 +1259,14 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
|
||||
/* FIXME: clear interrupts or what??? */
|
||||
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
spin_unlock(codec->lock);
|
||||
|
||||
snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
|
||||
snd_azf3328_codec_setdmaa(codec, runtime->dma_addr,
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
snd_pcm_lib_buffer_bytes(substream)
|
||||
);
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
spin_lock(codec->lock);
|
||||
#ifdef WIN9X
|
||||
/* FIXME: enable playback/recording??? */
|
||||
flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
|
||||
|
@ -1242,10 +1290,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
DMA_EPILOGUE_SOMETHING |
|
||||
DMA_SOMETHING_ELSE);
|
||||
#endif
|
||||
spin_unlock(&chip->reg_lock);
|
||||
snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
|
||||
spin_unlock(codec->lock);
|
||||
snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
|
||||
|
||||
if (is_playback_codec) {
|
||||
if (is_main_mixer_playback_codec) {
|
||||
/* now unmute WaveOut */
|
||||
if (!previously_muted)
|
||||
snd_azf3328_mixer_set_mute(
|
||||
|
@ -1258,19 +1306,19 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
|
||||
/* resume codec if we were active */
|
||||
spin_lock(&chip->reg_lock);
|
||||
spin_lock(codec->lock);
|
||||
if (codec->running)
|
||||
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
|
||||
snd_azf3328_codec_inw(
|
||||
codec, IDX_IO_CODEC_DMA_FLAGS
|
||||
) | DMA_RESUME
|
||||
);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
spin_unlock(codec->lock);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_azf3328_dbgcodec("STOP %s\n", codec->name);
|
||||
|
||||
if (is_playback_codec) {
|
||||
if (is_main_mixer_playback_codec) {
|
||||
/* mute WaveOut (avoid clicking during setup) */
|
||||
previously_muted =
|
||||
snd_azf3328_mixer_set_mute(
|
||||
|
@ -1278,7 +1326,7 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
);
|
||||
}
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
spin_lock(codec->lock);
|
||||
/* first, remember current value: */
|
||||
flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
|
||||
|
||||
|
@ -1293,10 +1341,10 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
|
||||
flags1 &= ~DMA_RUN_SOMETHING1;
|
||||
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
|
||||
spin_unlock(codec->lock);
|
||||
snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
|
||||
|
||||
if (is_playback_codec) {
|
||||
if (is_main_mixer_playback_codec) {
|
||||
/* now unmute WaveOut */
|
||||
if (!previously_muted)
|
||||
snd_azf3328_mixer_set_mute(
|
||||
|
@ -1330,67 +1378,29 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
|
|||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
|
||||
enum snd_azf3328_codec_type codec_type
|
||||
snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
|
||||
)
|
||||
{
|
||||
const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
|
||||
const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
|
||||
unsigned long bufptr, result;
|
||||
const struct snd_azf3328_codec_data *codec =
|
||||
substream->runtime->private_data;
|
||||
unsigned long result;
|
||||
snd_pcm_uframes_t frmres;
|
||||
|
||||
#ifdef QUERY_HARDWARE
|
||||
bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
|
||||
#else
|
||||
bufptr = substream->runtime->dma_addr;
|
||||
#endif
|
||||
result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
|
||||
|
||||
/* calculate offset */
|
||||
result -= bufptr;
|
||||
#ifdef QUERY_HARDWARE
|
||||
result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
|
||||
#else
|
||||
result -= codec->dma_base;
|
||||
#endif
|
||||
frmres = bytes_to_frames( substream->runtime, result);
|
||||
snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
|
||||
codec->name, result, frmres);
|
||||
snd_azf3328_dbgcodec("%08li %s @ 0x%8lx, frames %8ld\n",
|
||||
jiffies, codec->name, result, frmres);
|
||||
return frmres;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
#ifdef SUPPORT_GAMEPORT
|
||||
|
@ -1532,7 +1542,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
|
|||
}
|
||||
}
|
||||
|
||||
/* trigger next axes sampling, to be evaluated the next time we
|
||||
/* trigger next sampling of axes, to be evaluated the next time we
|
||||
* enter this function */
|
||||
|
||||
/* for some very, very strange reason we cannot enable
|
||||
|
@ -1624,29 +1634,29 @@ snd_azf3328_irq_log_unknown_type(u8 which)
|
|||
}
|
||||
|
||||
static inline void
|
||||
snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
|
||||
snd_azf3328_pcm_interrupt(const struct snd_azf3328_codec_data *first_codec,
|
||||
u8 status
|
||||
)
|
||||
{
|
||||
u8 which;
|
||||
enum snd_azf3328_codec_type codec_type;
|
||||
const struct snd_azf3328_codec_data *codec;
|
||||
const struct snd_azf3328_codec_data *codec = first_codec;
|
||||
|
||||
for (codec_type = AZF_CODEC_PLAYBACK;
|
||||
codec_type <= AZF_CODEC_I2S_OUT;
|
||||
++codec_type) {
|
||||
++codec_type, ++codec) {
|
||||
|
||||
/* skip codec if there's no interrupt for it */
|
||||
if (!(status & (1 << codec_type)))
|
||||
continue;
|
||||
|
||||
codec = &chip->codecs[codec_type];
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
spin_lock(codec->lock);
|
||||
which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
|
||||
/* ack all IRQ types immediately */
|
||||
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
spin_unlock(codec->lock);
|
||||
|
||||
if ((chip->pcm[codec_type]) && (codec->substream)) {
|
||||
if (codec->substream) {
|
||||
snd_pcm_period_elapsed(codec->substream);
|
||||
snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
|
||||
codec->name,
|
||||
|
@ -1701,7 +1711,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
|
||||
snd_azf3328_codec_interrupt(chip, status);
|
||||
snd_azf3328_pcm_interrupt(chip->codecs, status);
|
||||
|
||||
if (status & IRQ_GAMEPORT)
|
||||
snd_azf3328_gameport_interrupt(chip);
|
||||
|
@ -1789,101 +1799,85 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
|
||||
|
||||
snd_azf3328_dbgcallenter();
|
||||
chip->codecs[codec_type].substream = substream;
|
||||
codec->substream = substream;
|
||||
|
||||
/* same parameters for all our codecs - at least we think so... */
|
||||
runtime->hw = snd_azf3328_hardware;
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&snd_azf3328_hw_constraints_rates);
|
||||
runtime->private_data = codec;
|
||||
snd_azf3328_dbgcallleave();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_playback_open(struct snd_pcm_substream *substream)
|
||||
snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_capture_open(struct snd_pcm_substream *substream)
|
||||
snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
|
||||
snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
|
||||
enum snd_azf3328_codec_type codec_type
|
||||
snd_azf3328_pcm_close(struct snd_pcm_substream *substream
|
||||
)
|
||||
{
|
||||
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_azf3328_codec_data *codec =
|
||||
substream->runtime->private_data;
|
||||
|
||||
snd_azf3328_dbgcallenter();
|
||||
chip->codecs[codec_type].substream = NULL;
|
||||
codec->substream = NULL;
|
||||
snd_azf3328_dbgcallleave();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
|
||||
}
|
||||
|
||||
static int
|
||||
snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
static struct snd_pcm_ops snd_azf3328_playback_ops = {
|
||||
.open = snd_azf3328_playback_open,
|
||||
.close = snd_azf3328_playback_close,
|
||||
.open = snd_azf3328_pcm_playback_open,
|
||||
.close = snd_azf3328_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_azf3328_hw_params,
|
||||
.hw_free = snd_azf3328_hw_free,
|
||||
.prepare = snd_azf3328_codec_prepare,
|
||||
.trigger = snd_azf3328_codec_playback_trigger,
|
||||
.pointer = snd_azf3328_codec_playback_pointer
|
||||
.prepare = snd_azf3328_pcm_prepare,
|
||||
.trigger = snd_azf3328_pcm_trigger,
|
||||
.pointer = snd_azf3328_pcm_pointer
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops snd_azf3328_capture_ops = {
|
||||
.open = snd_azf3328_capture_open,
|
||||
.close = snd_azf3328_capture_close,
|
||||
.open = snd_azf3328_pcm_capture_open,
|
||||
.close = snd_azf3328_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_azf3328_hw_params,
|
||||
.hw_free = snd_azf3328_hw_free,
|
||||
.prepare = snd_azf3328_codec_prepare,
|
||||
.trigger = snd_azf3328_codec_capture_trigger,
|
||||
.pointer = snd_azf3328_codec_capture_pointer
|
||||
.prepare = snd_azf3328_pcm_prepare,
|
||||
.trigger = snd_azf3328_pcm_trigger,
|
||||
.pointer = snd_azf3328_pcm_pointer
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
|
||||
.open = snd_azf3328_i2s_out_open,
|
||||
.close = snd_azf3328_i2s_out_close,
|
||||
.open = snd_azf3328_pcm_i2s_out_open,
|
||||
.close = snd_azf3328_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_azf3328_hw_params,
|
||||
.hw_free = snd_azf3328_hw_free,
|
||||
.prepare = snd_azf3328_codec_prepare,
|
||||
.trigger = snd_azf3328_codec_i2s_out_trigger,
|
||||
.pointer = snd_azf3328_codec_i2s_out_pointer
|
||||
.prepare = snd_azf3328_pcm_prepare,
|
||||
.trigger = snd_azf3328_pcm_trigger,
|
||||
.pointer = snd_azf3328_pcm_pointer
|
||||
};
|
||||
|
||||
static int __devinit
|
||||
|
@ -1966,7 +1960,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
|
|||
snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
|
||||
delay = 49; /* minimum time is 49 ticks */
|
||||
}
|
||||
snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
|
||||
snd_azf3328_dbgtimer("setting timer countdown value %d\n", delay);
|
||||
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
|
||||
|
@ -2180,6 +2174,7 @@ snd_azf3328_create(struct snd_card *card,
|
|||
};
|
||||
u8 dma_init;
|
||||
enum snd_azf3328_codec_type codec_type;
|
||||
struct snd_azf3328_codec_data *codec_setup;
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
|
@ -2217,15 +2212,23 @@ snd_azf3328_create(struct snd_card *card,
|
|||
chip->opl3_io = pci_resource_start(pci, 3);
|
||||
chip->mixer_io = pci_resource_start(pci, 4);
|
||||
|
||||
chip->codecs[AZF_CODEC_PLAYBACK].io_base =
|
||||
chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
|
||||
chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
|
||||
chip->codecs[AZF_CODEC_CAPTURE].io_base =
|
||||
chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
|
||||
chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
|
||||
chip->codecs[AZF_CODEC_I2S_OUT].io_base =
|
||||
chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
|
||||
chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
|
||||
codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
|
||||
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
|
||||
codec_setup->lock = &chip->reg_lock;
|
||||
codec_setup->type = AZF_CODEC_PLAYBACK;
|
||||
codec_setup->name = "PLAYBACK";
|
||||
|
||||
codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
|
||||
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
|
||||
codec_setup->lock = &chip->reg_lock;
|
||||
codec_setup->type = AZF_CODEC_CAPTURE;
|
||||
codec_setup->name = "CAPTURE";
|
||||
|
||||
codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
|
||||
codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
|
||||
codec_setup->lock = &chip->reg_lock;
|
||||
codec_setup->type = AZF_CODEC_I2S_OUT;
|
||||
codec_setup->name = "I2S_OUT";
|
||||
|
||||
if (request_irq(pci->irq, snd_azf3328_interrupt,
|
||||
IRQF_SHARED, card->shortname, chip)) {
|
||||
|
@ -2257,15 +2260,15 @@ snd_azf3328_create(struct snd_card *card,
|
|||
struct snd_azf3328_codec_data *codec =
|
||||
&chip->codecs[codec_type];
|
||||
|
||||
/* shutdown codecs to save power */
|
||||
/* shutdown codecs to reduce power / noise */
|
||||
/* have ...ctrl_codec_activity() act properly */
|
||||
codec->running = 1;
|
||||
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
spin_lock_irq(codec->lock);
|
||||
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
|
||||
dma_init);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
spin_unlock_irq(codec->lock);
|
||||
}
|
||||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
@ -2419,6 +2422,7 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
/* same pcm object for playback/capture */
|
||||
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
|
||||
snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
|
||||
|
||||
|
|
|
@ -637,15 +637,9 @@ static struct snd_kcontrol_new snd_bt87x_capture_boost = {
|
|||
static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
|
||||
static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 3;
|
||||
if (info->value.enumerated.item > 2)
|
||||
info->value.enumerated.item = 2;
|
||||
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 3, texts);
|
||||
}
|
||||
|
||||
static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,
|
||||
|
|
|
@ -2507,14 +2507,12 @@ static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
|
||||
static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
static const char *const texts[3] = {
|
||||
"Line-In", "Rear Output", "Bass Output"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(uinfo, 1,
|
||||
cm->chip_version >= 39 ? 3 : 2, texts);
|
||||
}
|
||||
|
||||
static inline unsigned int get_line_in_mode(struct cmipci *cm)
|
||||
|
@ -2564,14 +2562,9 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
|
|||
static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[2] = { "Mic-In", "Center/LFE Output" };
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 2;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
static const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
|
||||
|
||||
return snd_ctl_enum_info(uinfo, 1, 2, texts);
|
||||
}
|
||||
|
||||
static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
|
||||
|
|
|
@ -1235,7 +1235,8 @@ static int azx_setup_periods(struct azx *chip,
|
|||
pos_adj = 0;
|
||||
} else {
|
||||
ofs = setup_bdle(substream, azx_dev,
|
||||
&bdl, ofs, pos_adj, 1);
|
||||
&bdl, ofs, pos_adj,
|
||||
!substream->runtime->no_period_wakeup);
|
||||
if (ofs < 0)
|
||||
goto error;
|
||||
}
|
||||
|
@ -1247,7 +1248,8 @@ static int azx_setup_periods(struct azx *chip,
|
|||
period_bytes - pos_adj, 0);
|
||||
else
|
||||
ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
|
||||
period_bytes, 1);
|
||||
period_bytes,
|
||||
!substream->runtime->no_period_wakeup);
|
||||
if (ofs < 0)
|
||||
goto error;
|
||||
}
|
||||
|
@ -1515,7 +1517,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
|||
/* No full-resume yet implemented */
|
||||
/* SNDRV_PCM_INFO_RESUME |*/
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
|
|
|
@ -10857,6 +10857,9 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
|
||||
const struct auto_pin_cfg *cfg);
|
||||
|
||||
/* almost identical with ALC880 parser... */
|
||||
static int alc882_parse_auto_config(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -10874,6 +10877,9 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
|
|||
err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (codec->vendor_id == 0x10ec0887)
|
||||
err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
|
||||
else
|
||||
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -17043,7 +17049,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
|
|||
#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
|
||||
|
||||
/* add playback controls from the parsed DAC table */
|
||||
/* Based on ALC880 version. But ALC861VD has separate,
|
||||
/* Based on ALC880 version. But ALC861VD and ALC887 have separate,
|
||||
* different NIDs for mute/unmute switch and volume control */
|
||||
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
|
||||
const struct auto_pin_cfg *cfg)
|
||||
|
|
|
@ -263,8 +263,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
|
|||
return;
|
||||
snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
|
||||
!spec->vt1708_jack_detectect);
|
||||
cancel_delayed_work(&spec->vt1708_hp_work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&spec->vt1708_hp_work);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -96,6 +96,11 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
|
|||
tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
|
||||
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
|
||||
ICE1712_DELTA_66E_CS_CHIP_B;
|
||||
tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
|
||||
tmp &= ~ICE1712_VX442_CS_DIGITAL;
|
||||
|
@ -119,6 +124,9 @@ static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
|
|||
case ICE1712_SUBDEVICE_DELTA410:
|
||||
tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
tmp |= ICE1712_DELTA_66E_CS_CS8427;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
tmp |= ICE1712_VX442_CS_DIGITAL;
|
||||
break;
|
||||
|
@ -275,6 +283,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
|
|||
priv->cs_addr = chip << 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* AK4524 on Delta66 rev E to choose the chip address
|
||||
*/
|
||||
static void delta66e_ak4524_lock(struct snd_akm4xxx *ak, int chip)
|
||||
{
|
||||
struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
|
||||
struct snd_ice1712 *ice = ak->private_data[0];
|
||||
|
||||
snd_ice1712_save_gpio_status(ice);
|
||||
priv->cs_mask =
|
||||
priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
|
||||
ICE1712_DELTA_66E_CS_CHIP_B;
|
||||
}
|
||||
|
||||
/*
|
||||
* AK4528 on VX442 to choose the chip mask
|
||||
*/
|
||||
|
@ -487,6 +509,29 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
|
|||
.mask_flags = 0,
|
||||
};
|
||||
|
||||
static struct snd_akm4xxx akm_delta66e __devinitdata = {
|
||||
.type = SND_AK4524,
|
||||
.num_adcs = 4,
|
||||
.num_dacs = 4,
|
||||
.ops = {
|
||||
.lock = delta66e_ak4524_lock,
|
||||
.set_rate_val = delta_ak4524_set_rate_val
|
||||
}
|
||||
};
|
||||
|
||||
static struct snd_ak4xxx_private akm_delta66e_priv __devinitdata = {
|
||||
.caddr = 2,
|
||||
.cif = 0, /* the default level of the CIF pin from AK4524 */
|
||||
.data_mask = ICE1712_DELTA_66E_DOUT,
|
||||
.clk_mask = ICE1712_DELTA_66E_CCLK,
|
||||
.cs_mask = 0,
|
||||
.cs_addr = 0, /* set later */
|
||||
.cs_none = 0,
|
||||
.add_flags = 0,
|
||||
.mask_flags = 0,
|
||||
};
|
||||
|
||||
|
||||
static struct snd_akm4xxx akm_delta44 __devinitdata = {
|
||||
.type = SND_AK4524,
|
||||
.num_adcs = 4,
|
||||
|
@ -644,9 +689,11 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
|
||||
break;
|
||||
default:
|
||||
snd_BUG();
|
||||
return -EINVAL;
|
||||
|
|
|
@ -144,6 +144,17 @@ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
|
|||
#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
|
||||
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
|
||||
|
||||
/* M-Audio Delta 66 rev. E definitions.
|
||||
* Newer revisions of Delta 66 have CS8427 over SPI for
|
||||
* S/PDIF transceiver instead of CS8404/CS8414. */
|
||||
/* 0x01 = DFS */
|
||||
#define ICE1712_DELTA_66E_CCLK 0x02 /* SPI clock */
|
||||
#define ICE1712_DELTA_66E_DIN 0x04 /* data input */
|
||||
#define ICE1712_DELTA_66E_DOUT 0x08 /* data output */
|
||||
#define ICE1712_DELTA_66E_CS_CS8427 0x10 /* chip select, low = CS8427 */
|
||||
#define ICE1712_DELTA_66E_CS_CHIP_A 0x20 /* AK4524 #0 */
|
||||
#define ICE1712_DELTA_66E_CS_CHIP_B 0x40 /* AK4524 #1 */
|
||||
|
||||
/* Digigram VX442 definitions */
|
||||
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
|
||||
#define ICE1712_VX442_DIN 0x04 /* data input */
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
|
||||
snd-hifier-objs := hifier.o
|
||||
snd-oxygen-objs := oxygen.o
|
||||
snd-oxygen-objs := oxygen.o xonar_dg.o
|
||||
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
|
||||
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
|
||||
|
||||
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
|
||||
obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
|
||||
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
|
||||
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -28,10 +28,23 @@
|
|||
*
|
||||
* GPIO 0 -> DFS0 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:
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
@ -41,18 +54,22 @@
|
|||
#include <sound/ac97_codec.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "oxygen.h"
|
||||
#include "xonar_dg.h"
|
||||
#include "ak4396.h"
|
||||
#include "wm8785.h"
|
||||
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_DESCRIPTION("C-Media CMI8788 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
|
||||
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
|
||||
",{C-Media,CMI8787}"
|
||||
",{C-Media,CMI8788}}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
|
@ -66,24 +83,46 @@ module_param_array(enable, bool, NULL, 0444);
|
|||
MODULE_PARM_DESC(enable, "enable card");
|
||||
|
||||
enum {
|
||||
MODEL_CMEDIA_REF, /* C-Media's reference design */
|
||||
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
|
||||
MODEL_CLARO, /* HT-Omega Claro */
|
||||
MODEL_CLARO_HALO, /* HT-Omega Claro halo */
|
||||
MODEL_CMEDIA_REF,
|
||||
MODEL_MERIDIAN,
|
||||
MODEL_MERIDIAN_2G,
|
||||
MODEL_CLARO,
|
||||
MODEL_CLARO_HALO,
|
||||
MODEL_FANTASIA,
|
||||
MODEL_SERENADE,
|
||||
MODEL_2CH_OUTPUT,
|
||||
MODEL_HG2PCI,
|
||||
MODEL_XONAR_DG,
|
||||
};
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
|
||||
/* C-Media's reference design */
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
|
||||
/* Asus Xonar DG */
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
|
||||
/* PCI 2.0 HD Audio */
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
|
||||
/* Kuroutoshikou CMI8787-HG2PCI */
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
|
||||
/* TempoTec HiFier Fantasia */
|
||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
|
||||
/* TempoTec HiFier Serenade */
|
||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
|
||||
/* AuzenTech X-Meridian */
|
||||
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
|
||||
/* AuzenTech X-Meridian 2G */
|
||||
{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
|
||||
/* HT-Omega Claro */
|
||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
|
||||
/* HT-Omega Claro halo */
|
||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
|
||||
{ }
|
||||
};
|
||||
|
@ -95,9 +134,15 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
|
|||
#define GPIO_AK5385_DFS_DOUBLE 0x0001
|
||||
#define GPIO_AK5385_DFS_QUAD 0x0002
|
||||
|
||||
#define GPIO_MERIDIAN_DIG_MASK 0x0050
|
||||
#define GPIO_MERIDIAN_DIG_EXT 0x0010
|
||||
#define GPIO_MERIDIAN_DIG_BOARD 0x0040
|
||||
|
||||
#define GPIO_CLARO_DIG_COAX 0x0040
|
||||
#define GPIO_CLARO_HP 0x0100
|
||||
|
||||
struct generic_data {
|
||||
unsigned int dacs;
|
||||
u8 ak4396_regs[4][5];
|
||||
u16 wm8785_regs[3];
|
||||
};
|
||||
|
@ -148,7 +193,7 @@ static void ak4396_registers_init(struct oxygen *chip)
|
|||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2,
|
||||
|
@ -166,6 +211,7 @@ static void ak4396_init(struct oxygen *chip)
|
|||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
data->dacs = chip->model.dac_channels_pcm / 2;
|
||||
data->ak4396_regs[0][AK4396_CONTROL_2] =
|
||||
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
ak4396_registers_init(chip);
|
||||
|
@ -207,6 +253,10 @@ static void generic_init(struct oxygen *chip)
|
|||
|
||||
static void meridian_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_MERIDIAN_DIG_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
|
||||
ak4396_init(chip);
|
||||
ak5385_init(chip);
|
||||
}
|
||||
|
@ -220,6 +270,8 @@ static void claro_enable_hp(struct oxygen *chip)
|
|||
|
||||
static void claro_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
|
||||
ak4396_init(chip);
|
||||
wm8785_init(chip);
|
||||
claro_enable_hp(chip);
|
||||
|
@ -227,11 +279,24 @@ static void claro_init(struct oxygen *chip)
|
|||
|
||||
static void claro_halo_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
|
||||
ak4396_init(chip);
|
||||
ak5385_init(chip);
|
||||
claro_enable_hp(chip);
|
||||
}
|
||||
|
||||
static void fantasia_init(struct oxygen *chip)
|
||||
{
|
||||
ak4396_init(chip);
|
||||
snd_component_add(chip->card, "CS5340");
|
||||
}
|
||||
|
||||
static void stereo_output_init(struct oxygen *chip)
|
||||
{
|
||||
ak4396_init(chip);
|
||||
}
|
||||
|
||||
static void generic_cleanup(struct oxygen *chip)
|
||||
{
|
||||
}
|
||||
|
@ -268,6 +333,11 @@ static void claro_resume(struct oxygen *chip)
|
|||
claro_enable_hp(chip);
|
||||
}
|
||||
|
||||
static void stereo_resume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_registers_init(chip);
|
||||
}
|
||||
|
||||
static void set_ak4396_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -286,7 +356,7 @@ static void set_ak4396_params(struct oxygen *chip,
|
|||
msleep(1); /* wait for the new MCLK to become stable */
|
||||
|
||||
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, value);
|
||||
|
@ -298,9 +368,10 @@ static void set_ak4396_params(struct oxygen *chip,
|
|||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write_cached(chip, i, AK4396_LCH_ATT,
|
||||
chip->dac_volume[i * 2]);
|
||||
ak4396_write_cached(chip, i, AK4396_RCH_ATT,
|
||||
|
@ -317,7 +388,7 @@ static void update_ak4396_mute(struct oxygen *chip)
|
|||
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
|
||||
if (chip->dac_mute)
|
||||
value |= AK4396_SMUTE;
|
||||
for (i = 0; i < 4; ++i)
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
|
||||
}
|
||||
|
||||
|
@ -356,6 +427,10 @@ static void set_ak5385_params(struct oxygen *chip,
|
|||
value, GPIO_AK5385_DFS_MASK);
|
||||
}
|
||||
|
||||
static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
|
||||
{
|
||||
}
|
||||
|
||||
static int rolloff_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
|
@ -363,13 +438,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
|
|||
"Sharp Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
|
@ -400,7 +469,7 @@ static int rolloff_put(struct snd_kcontrol *ctl,
|
|||
reg &= ~AK4396_SLOW;
|
||||
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
|
||||
if (changed) {
|
||||
for (i = 0; i < 4; ++i)
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
@ -421,13 +490,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
|||
"None", "High-pass Filter"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
|
@ -466,6 +529,100 @@ static const struct snd_kcontrol_new hpf_control = {
|
|||
.put = hpf_put,
|
||||
};
|
||||
|
||||
static int meridian_dig_source_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "On-board", "Extension" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int claro_dig_source_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "Optical", "Coaxial" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int meridian_dig_source_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
|
||||
GPIO_MERIDIAN_DIG_EXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_dig_source_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
|
||||
GPIO_CLARO_DIG_COAX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meridian_dig_source_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 old_reg, new_reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
|
||||
if (value->value.enumerated.item[0] == 0)
|
||||
new_reg |= GPIO_MERIDIAN_DIG_BOARD;
|
||||
else
|
||||
new_reg |= GPIO_MERIDIAN_DIG_EXT;
|
||||
changed = new_reg != old_reg;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int claro_dig_source_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 old_reg, new_reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
|
||||
if (value->value.enumerated.item[0])
|
||||
new_reg |= GPIO_CLARO_DIG_COAX;
|
||||
changed = new_reg != old_reg;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new meridian_dig_source_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "IEC958 Source Capture Enum",
|
||||
.info = meridian_dig_source_info,
|
||||
.get = meridian_dig_source_get,
|
||||
.put = meridian_dig_source_put,
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new claro_dig_source_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "IEC958 Source Capture Enum",
|
||||
.info = claro_dig_source_info,
|
||||
.get = claro_dig_source_get,
|
||||
.put = claro_dig_source_put,
|
||||
};
|
||||
|
||||
static int generic_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
|
@ -484,6 +641,81 @@ static int generic_wm8785_mixer_init(struct oxygen *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int meridian_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&meridian_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_wm8785_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&claro_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_halo_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&claro_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_ak4396_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int dac, i;
|
||||
|
||||
for (dac = 0; dac < data->dacs; ++dac) {
|
||||
snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
|
||||
for (i = 0; i < 5; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_wm8785_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nWM8785:");
|
||||
for (i = 0; i < 3; ++i)
|
||||
snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_oxygen_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
dump_ak4396_registers(chip, buffer);
|
||||
dump_wm8785_registers(chip, buffer);
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
|
||||
|
||||
static const struct oxygen_model model_generic = {
|
||||
|
@ -494,11 +726,11 @@ static const struct oxygen_model model_generic = {
|
|||
.mixer_init = generic_wm8785_mixer_init,
|
||||
.cleanup = generic_cleanup,
|
||||
.resume = generic_resume,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_ak4396_params,
|
||||
.set_adc_params = set_wm8785_params,
|
||||
.update_dac_volume = update_ak4396_volume,
|
||||
.update_dac_mute = update_ak4396_mute,
|
||||
.dump_registers = dump_oxygen_registers,
|
||||
.dac_tlv = ak4396_db_scale,
|
||||
.model_data_size = sizeof(struct generic_data),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
|
@ -508,11 +740,14 @@ static const struct oxygen_model model_generic = {
|
|||
CAPTURE_1_FROM_SPDIF |
|
||||
CAPTURE_2_FROM_AC97_1 |
|
||||
AC97_CD_INPUT,
|
||||
.dac_channels = 8,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 0,
|
||||
.dac_volume_max = 255,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI |
|
||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
@ -520,42 +755,87 @@ static const struct oxygen_model model_generic = {
|
|||
static int __devinit get_oxygen_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
static const char *const names[] = {
|
||||
[MODEL_MERIDIAN] = "AuzenTech X-Meridian",
|
||||
[MODEL_MERIDIAN_2G] = "AuzenTech X-Meridian 2G",
|
||||
[MODEL_CLARO] = "HT-Omega Claro",
|
||||
[MODEL_CLARO_HALO] = "HT-Omega Claro halo",
|
||||
[MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
|
||||
[MODEL_SERENADE] = "TempoTec HiFier Serenade",
|
||||
[MODEL_HG2PCI] = "CMI8787-HG2PCI",
|
||||
};
|
||||
|
||||
chip->model = model_generic;
|
||||
switch (id->driver_data) {
|
||||
case MODEL_MERIDIAN:
|
||||
case MODEL_MERIDIAN_2G:
|
||||
chip->model.init = meridian_init;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.mixer_init = meridian_mixer_init;
|
||||
chip->model.resume = meridian_resume;
|
||||
chip->model.set_adc_params = set_ak5385_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF;
|
||||
if (id->driver_data == MODEL_MERIDIAN)
|
||||
chip->model.device_config |= AC97_CD_INPUT;
|
||||
break;
|
||||
case MODEL_CLARO:
|
||||
chip->model.init = claro_init;
|
||||
chip->model.mixer_init = claro_mixer_init;
|
||||
chip->model.cleanup = claro_cleanup;
|
||||
chip->model.suspend = claro_suspend;
|
||||
chip->model.resume = claro_resume;
|
||||
break;
|
||||
case MODEL_CLARO_HALO:
|
||||
chip->model.init = claro_halo_init;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.mixer_init = claro_halo_mixer_init;
|
||||
chip->model.cleanup = claro_cleanup;
|
||||
chip->model.suspend = claro_suspend;
|
||||
chip->model.resume = claro_resume;
|
||||
chip->model.set_adc_params = set_ak5385_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF;
|
||||
break;
|
||||
case MODEL_FANTASIA:
|
||||
case MODEL_SERENADE:
|
||||
case MODEL_2CH_OUTPUT:
|
||||
case MODEL_HG2PCI:
|
||||
chip->model.shortname = "C-Media CMI8787";
|
||||
chip->model.chip = "CMI8787";
|
||||
if (id->driver_data == MODEL_FANTASIA)
|
||||
chip->model.init = fantasia_init;
|
||||
else
|
||||
chip->model.init = stereo_output_init;
|
||||
chip->model.resume = stereo_resume;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.set_adc_params = set_no_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF;
|
||||
if (id->driver_data == MODEL_FANTASIA) {
|
||||
chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
|
||||
chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
|
||||
}
|
||||
chip->model.dac_channels_pcm = 2;
|
||||
chip->model.dac_channels_mixer = 2;
|
||||
break;
|
||||
case MODEL_XONAR_DG:
|
||||
chip->model = model_xonar_dg;
|
||||
break;
|
||||
}
|
||||
if (id->driver_data == MODEL_MERIDIAN ||
|
||||
id->driver_data == MODEL_MERIDIAN_2G ||
|
||||
id->driver_data == MODEL_CLARO_HALO) {
|
||||
chip->model.misc_flags = OXYGEN_MISC_MIDI;
|
||||
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
|
||||
}
|
||||
if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
|
||||
chip->model.shortname = names[id->driver_data];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#define PCM_AC97 5
|
||||
#define PCM_COUNT 6
|
||||
|
||||
#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \
|
||||
(MCLK_##f_double << 2) | \
|
||||
(MCLK_##f_quad << 4))
|
||||
|
||||
#define OXYGEN_IO_SIZE 0x100
|
||||
|
||||
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
|
||||
|
@ -35,6 +39,7 @@
|
|||
#define MIDI_OUTPUT 0x0800
|
||||
#define MIDI_INPUT 0x1000
|
||||
#define AC97_CD_INPUT 0x2000
|
||||
#define AC97_FMIC_SWITCH 0x4000
|
||||
|
||||
enum {
|
||||
CONTROL_SPDIF_PCM,
|
||||
|
@ -65,6 +70,7 @@ struct snd_pcm_hardware;
|
|||
struct snd_pcm_hw_params;
|
||||
struct snd_kcontrol_new;
|
||||
struct snd_rawmidi;
|
||||
struct snd_info_buffer;
|
||||
struct oxygen;
|
||||
|
||||
struct oxygen_model {
|
||||
|
@ -79,8 +85,6 @@ struct oxygen_model {
|
|||
void (*resume)(struct oxygen *chip);
|
||||
void (*pcm_hardware_filter)(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware);
|
||||
unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params);
|
||||
void (*set_dac_params)(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void (*set_adc_params)(struct oxygen *chip,
|
||||
|
@ -92,15 +96,19 @@ struct oxygen_model {
|
|||
void (*uart_input)(struct oxygen *chip);
|
||||
void (*ac97_switch)(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int mute);
|
||||
void (*dump_registers)(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer);
|
||||
const unsigned int *dac_tlv;
|
||||
unsigned long private_data;
|
||||
size_t model_data_size;
|
||||
unsigned int device_config;
|
||||
u8 dac_channels;
|
||||
u8 dac_channels_pcm;
|
||||
u8 dac_channels_mixer;
|
||||
u8 dac_volume_min;
|
||||
u8 dac_volume_max;
|
||||
u8 misc_flags;
|
||||
u8 function_flags;
|
||||
u8 dac_mclks;
|
||||
u8 adc_mclks;
|
||||
u16 dac_i2s_format;
|
||||
u16 adc_i2s_format;
|
||||
};
|
||||
|
@ -121,7 +129,6 @@ struct oxygen {
|
|||
u8 pcm_running;
|
||||
u8 dac_routing;
|
||||
u8 spdif_playback_enable;
|
||||
u8 revision;
|
||||
u8 has_ac97_0;
|
||||
u8 has_ac97_1;
|
||||
u32 spdif_bits;
|
||||
|
@ -167,8 +174,6 @@ void oxygen_update_spdif_source(struct oxygen *chip);
|
|||
/* oxygen_pcm.c */
|
||||
|
||||
int oxygen_pcm_init(struct oxygen *chip);
|
||||
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params);
|
||||
|
||||
/* oxygen_io.c */
|
||||
|
||||
|
|
|
@ -197,11 +197,11 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
|
|||
{
|
||||
unsigned int count;
|
||||
|
||||
/* should not need more than 7.68 us (24 * 320 ns) */
|
||||
/* should not need more than 30.72 us (24 * 1.28 us) */
|
||||
count = 10;
|
||||
while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
|
||||
&& count > 0) {
|
||||
udelay(1);
|
||||
udelay(4);
|
||||
--count;
|
||||
}
|
||||
|
||||
|
|
|
@ -202,7 +202,13 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
|
|||
struct oxygen *chip = entry->private_data;
|
||||
int i, j;
|
||||
|
||||
snd_iprintf(buffer, "CMI8788\n\n");
|
||||
switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) {
|
||||
case OXYGEN_PACKAGE_ID_8786: i = '6'; break;
|
||||
case OXYGEN_PACKAGE_ID_8787: i = '7'; break;
|
||||
case OXYGEN_PACKAGE_ID_8788: i = '8'; break;
|
||||
default: i = '?'; break;
|
||||
}
|
||||
snd_iprintf(buffer, "CMI878%c:\n", i);
|
||||
for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; ++j)
|
||||
|
@ -212,7 +218,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
|
|||
if (mutex_lock_interruptible(&chip->mutex) < 0)
|
||||
return;
|
||||
if (chip->has_ac97_0) {
|
||||
snd_iprintf(buffer, "\nAC97\n");
|
||||
snd_iprintf(buffer, "\nAC97:\n");
|
||||
for (i = 0; i < 0x80; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; j += 2)
|
||||
|
@ -222,7 +228,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
|
|||
}
|
||||
}
|
||||
if (chip->has_ac97_1) {
|
||||
snd_iprintf(buffer, "\nAC97 2\n");
|
||||
snd_iprintf(buffer, "\nAC97 2:\n");
|
||||
for (i = 0; i < 0x80; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; j += 2)
|
||||
|
@ -232,13 +238,15 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
|
|||
}
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
if (chip->model.dump_registers)
|
||||
chip->model.dump_registers(chip, buffer);
|
||||
}
|
||||
|
||||
static void oxygen_proc_init(struct oxygen *chip)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if (!snd_card_proc_new(chip->card, "cmi8788", &entry))
|
||||
if (!snd_card_proc_new(chip->card, "oxygen", &entry))
|
||||
snd_info_set_text_ops(entry, chip, oxygen_proc_read);
|
||||
}
|
||||
#else
|
||||
|
@ -262,7 +270,7 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
|
|||
*/
|
||||
subdevice = oxygen_read_eeprom(chip, 2);
|
||||
/* use default ID if EEPROM is missing */
|
||||
if (subdevice == 0xffff)
|
||||
if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
|
||||
subdevice = 0x8788;
|
||||
/*
|
||||
* We use only the subsystem device ID for searching because it is
|
||||
|
@ -364,12 +372,7 @@ static void oxygen_init(struct oxygen *chip)
|
|||
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
|
||||
chip->spdif_pcm_bits = chip->spdif_bits;
|
||||
|
||||
if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2)
|
||||
chip->revision = 2;
|
||||
else
|
||||
chip->revision = 1;
|
||||
|
||||
if (chip->revision == 1)
|
||||
if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
|
||||
oxygen_set_bits8(chip, OXYGEN_MISC,
|
||||
OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
|
||||
|
||||
|
@ -406,28 +409,40 @@ static void oxygen_init(struct oxygen *chip)
|
|||
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
|
||||
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
|
||||
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
OXYGEN_RATE_48000 | chip->model.dac_i2s_format |
|
||||
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.dac_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.dac_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
else
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_2_FROM_I2S_2))
|
||||
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
|
||||
OXYGEN_RATE_48000 | chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
else
|
||||
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK);
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_OUT_ENABLE |
|
||||
OXYGEN_SPDIF_LOOPBACK);
|
||||
|
@ -557,7 +572,8 @@ static void oxygen_card_free(struct snd_card *card)
|
|||
oxygen_shutdown(chip);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
flush_scheduled_work();
|
||||
flush_work_sync(&chip->spdif_input_bits_work);
|
||||
flush_work_sync(&chip->gpio_work);
|
||||
chip->model.cleanup(chip);
|
||||
kfree(chip->model_data);
|
||||
mutex_destroy(&chip->mutex);
|
||||
|
@ -648,8 +664,8 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
|||
|
||||
strcpy(card->driver, chip->model.chip);
|
||||
strcpy(card->shortname, chip->model.shortname);
|
||||
sprintf(card->longname, "%s (rev %u) at %#lx, irq %i",
|
||||
chip->model.longname, chip->revision, chip->addr, chip->irq);
|
||||
sprintf(card->longname, "%s at %#lx, irq %i",
|
||||
chip->model.longname, chip->addr, chip->irq);
|
||||
strcpy(card->mixername, chip->model.chip);
|
||||
snd_component_add(card, chip->model.chip);
|
||||
|
||||
|
@ -733,7 +749,8 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
synchronize_irq(chip->irq);
|
||||
flush_scheduled_work();
|
||||
flush_work_sync(&chip->spdif_input_bits_work);
|
||||
flush_work_sync(&chip->gpio_work);
|
||||
chip->interrupt_mask = saved_interrupt_mask;
|
||||
|
||||
pci_disable_device(pci);
|
||||
|
|
|
@ -31,7 +31,7 @@ static int dac_volume_info(struct snd_kcontrol *ctl,
|
|||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = chip->model.dac_channels;
|
||||
info->count = chip->model.dac_channels_mixer;
|
||||
info->value.integer.min = chip->model.dac_volume_min;
|
||||
info->value.integer.max = chip->model.dac_volume_max;
|
||||
return 0;
|
||||
|
@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,
|
|||
unsigned int i;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
for (i = 0; i < chip->model.dac_channels; ++i)
|
||||
for (i = 0; i < chip->model.dac_channels_mixer; ++i)
|
||||
value->value.integer.value[i] = chip->dac_volume[i];
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
|
@ -59,7 +59,7 @@ static int dac_volume_put(struct snd_kcontrol *ctl,
|
|||
|
||||
changed = 0;
|
||||
mutex_lock(&chip->mutex);
|
||||
for (i = 0; i < chip->model.dac_channels; ++i)
|
||||
for (i = 0; i < chip->model.dac_channels_mixer; ++i)
|
||||
if (value->value.integer.value[i] != chip->dac_volume[i]) {
|
||||
chip->dac_volume[i] = value->value.integer.value[i];
|
||||
changed = 1;
|
||||
|
@ -97,6 +97,16 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
|
|||
return changed;
|
||||
}
|
||||
|
||||
static unsigned int upmix_item_count(struct oxygen *chip)
|
||||
{
|
||||
if (chip->model.dac_channels_pcm < 8)
|
||||
return 2;
|
||||
else if (chip->model.update_center_lfe_mix)
|
||||
return 5;
|
||||
else
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[5] = {
|
||||
|
@ -107,15 +117,9 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
|||
"Front+Surround+Center/LFE+Back",
|
||||
};
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
|
||||
unsigned int count = upmix_item_count(chip);
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = count;
|
||||
if (info->value.enumerated.item >= count)
|
||||
info->value.enumerated.item = count - 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, count, names);
|
||||
}
|
||||
|
||||
static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
|
@ -188,7 +192,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
|
|||
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
|
||||
unsigned int count = upmix_item_count(chip);
|
||||
int changed;
|
||||
|
||||
if (value->value.enumerated.item[0] >= count)
|
||||
|
@ -430,30 +434,31 @@ static int spdif_input_default_get(struct snd_kcontrol *ctl,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int spdif_loopback_get(struct snd_kcontrol *ctl,
|
||||
static int spdif_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u32 bit = ctl->private_value;
|
||||
|
||||
value->value.integer.value[0] =
|
||||
!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL)
|
||||
& OXYGEN_SPDIF_LOOPBACK);
|
||||
!!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) & bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spdif_loopback_put(struct snd_kcontrol *ctl,
|
||||
static int spdif_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u32 bit = ctl->private_value;
|
||||
u32 oldreg, newreg;
|
||||
int changed;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
|
||||
if (value->value.integer.value[0])
|
||||
newreg = oldreg | OXYGEN_SPDIF_LOOPBACK;
|
||||
newreg = oldreg | bit;
|
||||
else
|
||||
newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK;
|
||||
newreg = oldreg & ~bit;
|
||||
changed = newreg != oldreg;
|
||||
if (changed)
|
||||
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg);
|
||||
|
@ -644,6 +649,46 @@ static int ac97_volume_put(struct snd_kcontrol *ctl,
|
|||
return change;
|
||||
}
|
||||
|
||||
static int mic_fmic_source_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[] = { "Mic Jack", "Front Panel" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int mic_fmic_source_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] =
|
||||
!!(oxygen_read_ac97(chip, 0, CM9780_JACK) & CM9780_FMIC2MIC);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mic_fmic_source_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 oldreg, newreg;
|
||||
int change;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
oldreg = oxygen_read_ac97(chip, 0, CM9780_JACK);
|
||||
if (value->value.enumerated.item[0])
|
||||
newreg = oldreg | CM9780_FMIC2MIC;
|
||||
else
|
||||
newreg = oldreg & ~CM9780_FMIC2MIC;
|
||||
change = newreg != oldreg;
|
||||
if (change)
|
||||
oxygen_write_ac97(chip, 0, CM9780_JACK, newreg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return change;
|
||||
}
|
||||
|
||||
static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
|
@ -791,8 +836,17 @@ static const struct snd_kcontrol_new spdif_input_controls[] = {
|
|||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH),
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = spdif_loopback_get,
|
||||
.put = spdif_loopback_put,
|
||||
.get = spdif_bit_switch_get,
|
||||
.put = spdif_bit_switch_put,
|
||||
.private_value = OXYGEN_SPDIF_LOOPBACK,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("Validity Check ",CAPTURE,SWITCH),
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = spdif_bit_switch_get,
|
||||
.put = spdif_bit_switch_put,
|
||||
.private_value = OXYGEN_SPDIF_SPDVALID,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -908,6 +962,13 @@ static const struct snd_kcontrol_new ac97_controls[] = {
|
|||
AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC, 0),
|
||||
AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1),
|
||||
AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Mic Source Capture Enum",
|
||||
.info = mic_fmic_source_info,
|
||||
.get = mic_fmic_source_get,
|
||||
.put = mic_fmic_source_put,
|
||||
},
|
||||
AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1),
|
||||
AC97_VOLUME("CD Capture Volume", 0, AC97_CD, 1),
|
||||
AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1),
|
||||
|
@ -970,7 +1031,10 @@ static int add_controls(struct oxygen *chip,
|
|||
continue;
|
||||
}
|
||||
if (!strcmp(template.name, "Stereo Upmixing") &&
|
||||
chip->model.dac_channels == 2)
|
||||
chip->model.dac_channels_pcm == 2)
|
||||
continue;
|
||||
if (!strcmp(template.name, "Mic Source Capture Enum") &&
|
||||
!(chip->model.device_config & AC97_FMIC_SWITCH))
|
||||
continue;
|
||||
if (!strncmp(template.name, "CD Capture ", 11) &&
|
||||
!(chip->model.device_config & AC97_CD_INPUT))
|
||||
|
|
|
@ -39,7 +39,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START,
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
|
@ -65,7 +66,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START,
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
|
@ -91,7 +93,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START,
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
|
@ -140,7 +143,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,
|
|||
runtime->hw.rate_min = 44100;
|
||||
break;
|
||||
case PCM_MULTICH:
|
||||
runtime->hw.channels_max = chip->model.dac_channels;
|
||||
runtime->hw.channels_max = chip->model.dac_channels_pcm;
|
||||
break;
|
||||
}
|
||||
if (chip->model.pcm_hardware_filter)
|
||||
|
@ -271,17 +274,6 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
|
||||
unsigned int channel,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
if (params_rate(hw_params) <= 96000)
|
||||
return OXYGEN_I2S_MCLK_256;
|
||||
else
|
||||
return OXYGEN_I2S_MCLK_128;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_default_i2s_mclk);
|
||||
|
||||
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
|
||||
|
@ -341,6 +333,26 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u16 get_mclk(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int mclks, shift;
|
||||
|
||||
if (channel == PCM_MULTICH)
|
||||
mclks = chip->model.dac_mclks;
|
||||
else
|
||||
mclks = chip->model.adc_mclks;
|
||||
|
||||
if (params_rate(params) <= 48000)
|
||||
shift = 0;
|
||||
else if (params_rate(params) <= 96000)
|
||||
shift = 2;
|
||||
else
|
||||
shift = 4;
|
||||
|
||||
return OXYGEN_I2S_MCLK(mclks >> shift);
|
||||
}
|
||||
|
||||
static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
|
@ -357,8 +369,8 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
|
|||
OXYGEN_REC_FORMAT_A_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
get_mclk(chip, PCM_A, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
|
@ -393,9 +405,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
|
|||
if (!is_ac97)
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.get_i2s_mclk(chip, PCM_B,
|
||||
hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
get_mclk(chip, PCM_B, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
|
@ -476,8 +487,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
|
|||
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.dac_i2s_format |
|
||||
chip->model.get_i2s_mclk(chip, PCM_MULTICH,
|
||||
hw_params) |
|
||||
get_mclk(chip, PCM_MULTICH, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
|
@ -530,6 +540,9 @@ static int oxygen_prepare(struct snd_pcm_substream *substream)
|
|||
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
|
||||
if (substream->runtime->no_period_wakeup)
|
||||
chip->interrupt_mask &= ~channel_mask;
|
||||
else
|
||||
chip->interrupt_mask |= channel_mask;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
|
|
@ -139,9 +139,11 @@
|
|||
#define OXYGEN_I2S_FORMAT_I2S 0x0000
|
||||
#define OXYGEN_I2S_FORMAT_LJUST 0x0008
|
||||
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
|
||||
#define OXYGEN_I2S_MCLK_128 0x0000
|
||||
#define OXYGEN_I2S_MCLK_256 0x0010
|
||||
#define OXYGEN_I2S_MCLK_512 0x0020
|
||||
#define OXYGEN_I2S_MCLK_SHIFT 4
|
||||
#define MCLK_128 0
|
||||
#define MCLK_256 1
|
||||
#define MCLK_512 2
|
||||
#define OXYGEN_I2S_MCLK(f) (((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)
|
||||
#define OXYGEN_I2S_BITS_MASK 0x00c0
|
||||
#define OXYGEN_I2S_BITS_16 0x0000
|
||||
#define OXYGEN_I2S_BITS_20 0x0040
|
||||
|
@ -238,11 +240,11 @@
|
|||
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
|
||||
#define OXYGEN_SPI_DATA_LENGTH_2 0x00
|
||||
#define OXYGEN_SPI_DATA_LENGTH_3 0x02
|
||||
#define OXYGEN_SPI_CLOCK_MASK 0xc0
|
||||
#define OXYGEN_SPI_CLOCK_MASK 0x0c
|
||||
#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
|
||||
#define OXYGEN_SPI_CLOCK_320 0x40
|
||||
#define OXYGEN_SPI_CLOCK_640 0x80
|
||||
#define OXYGEN_SPI_CLOCK_1280 0xc0
|
||||
#define OXYGEN_SPI_CLOCK_320 0x04
|
||||
#define OXYGEN_SPI_CLOCK_640 0x08
|
||||
#define OXYGEN_SPI_CLOCK_1280 0x0c
|
||||
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
|
||||
#define OXYGEN_SPI_CODEC_SHIFT 4
|
||||
#define OXYGEN_SPI_CEN_MASK 0x80
|
||||
|
|
|
@ -24,6 +24,8 @@ void xonar_init_ext_power(struct oxygen *chip);
|
|||
void xonar_init_cs53x1(struct oxygen *chip);
|
||||
void xonar_set_cs53x1_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
|
||||
#define XONAR_GPIO_BIT_INVERT (1 << 16)
|
||||
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value);
|
||||
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
|
|
|
@ -22,28 +22,27 @@
|
|||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> CS4398 (front)
|
||||
* <-> CS4362A (surround, center/LFE, back)
|
||||
* I²C <-> CS4398 (addr 1001111) (front)
|
||||
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
|
||||
*
|
||||
* GPI 0 <- external power present (DX only)
|
||||
*
|
||||
* 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 3 -> M1 of CS5361
|
||||
* GPIO 6 -> ?
|
||||
* GPIO 7 -> ?
|
||||
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
|
||||
*
|
||||
* CS4398:
|
||||
*
|
||||
* AD0 <- 1
|
||||
* AD1 <- 1
|
||||
*
|
||||
* CS4362A:
|
||||
*
|
||||
* AD0 <- 0
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
@ -63,6 +62,7 @@
|
|||
#define GPI_EXT_POWER 0x01
|
||||
#define GPIO_D1_OUTPUT_ENABLE 0x0001
|
||||
#define GPIO_D1_FRONT_PANEL 0x0002
|
||||
#define GPIO_D1_MAGIC 0x00c0
|
||||
#define GPIO_D1_INPUT_ROUTE 0x0100
|
||||
|
||||
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
|
||||
|
@ -169,12 +169,12 @@ static void xonar_d1_init(struct oxygen *chip)
|
|||
cs43xx_registers_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
|
||||
GPIO_D1_FRONT_PANEL |
|
||||
GPIO_D1_MAGIC |
|
||||
GPIO_D1_INPUT_ROUTE);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
|
||||
|
||||
oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
|
||||
|
||||
xonar_init_cs53x1(chip);
|
||||
xonar_enable_output(chip);
|
||||
|
||||
|
@ -284,7 +284,7 @@ static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
|
|||
|
||||
static const struct snd_kcontrol_new front_panel_switch = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Front Panel Switch",
|
||||
.name = "Front Panel Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = xonar_gpio_bit_switch_get,
|
||||
.put = xonar_gpio_bit_switch_put,
|
||||
|
@ -298,13 +298,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
|
|||
"Fast Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
|
@ -380,6 +374,30 @@ static int xonar_d1_mixer_init(struct oxygen *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dump_cs4362a_registers(struct xonar_cs43xx *data,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4362A:");
|
||||
for (i = 1; i <= 14; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_d1_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4398: 7?");
|
||||
for (i = 2; i <= 8; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
dump_cs4362a_registers(data, buffer);
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_xonar_d1 = {
|
||||
.longname = "Asus Virtuoso 100",
|
||||
.chip = "AV200",
|
||||
|
@ -388,22 +406,26 @@ static const struct oxygen_model model_xonar_d1 = {
|
|||
.cleanup = xonar_d1_cleanup,
|
||||
.suspend = xonar_d1_suspend,
|
||||
.resume = xonar_d1_resume,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_cs43xx_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_cs43xx_volume,
|
||||
.update_dac_mute = update_cs43xx_mute,
|
||||
.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
|
||||
.ac97_switch = xonar_d1_line_mic_ac97_switch,
|
||||
.dump_registers = dump_d1_registers,
|
||||
.dac_tlv = cs4362a_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_cs43xx),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2,
|
||||
.dac_channels = 8,
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
AC97_FMIC_SWITCH,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 127 - 60,
|
||||
.dac_volume_max = 127,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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
|
|
@ -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>
|
||||
*
|
||||
|
|
|
@ -104,9 +104,10 @@ int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
|||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
|
||||
|
||||
value->value.integer.value[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -115,12 +116,13 @@ int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
|||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
|
||||
u16 old_bits, new_bits;
|
||||
int changed;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
if (value->value.integer.value[0])
|
||||
if (!!value->value.integer.value[0] ^ invert)
|
||||
new_bits = old_bits | bit;
|
||||
else
|
||||
new_bits = old_bits & ~bit;
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
*
|
||||
* CM9780:
|
||||
*
|
||||
* 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,22 +50,29 @@
|
|||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> PCM1796 (front)
|
||||
* I²C <-> PCM1796 (addr 1001100) (front)
|
||||
*
|
||||
* 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 3 -> M1 of CS5381
|
||||
* GPIO 4 <- daughterboard detection
|
||||
* GPIO 5 <- daughterboard detection
|
||||
* GPIO 6 -> ?
|
||||
* GPIO 7 -> ?
|
||||
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
|
||||
*
|
||||
* TXD -> HDMI controller
|
||||
* RXD <- HDMI controller
|
||||
*
|
||||
* PCM1796 front: AD1,0 <- 0,0
|
||||
* UART <-> HDMI controller
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* 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
|
||||
|
@ -73,13 +86,9 @@
|
|||
* GPIO 4 <- 0
|
||||
* GPIO 5 <- 0
|
||||
*
|
||||
* I²C <-> PCM1796 (surround)
|
||||
* <-> PCM1796 (center/LFE)
|
||||
* <-> PCM1796 (back)
|
||||
*
|
||||
* PCM1796 surround: AD1,0 <- 0,1
|
||||
* PCM1796 center/LFE: AD1,0 <- 1,0
|
||||
* PCM1796 back: AD1,0 <- 1,1
|
||||
* I²C <-> PCM1796 (addr 1001101) (surround)
|
||||
* <-> PCM1796 (addr 1001110) (center/LFE)
|
||||
* <-> PCM1796 (addr 1001111) (back)
|
||||
*
|
||||
* unknown daughterboard
|
||||
* ---------------------
|
||||
|
@ -87,9 +96,7 @@
|
|||
* GPIO 4 <- 0
|
||||
* GPIO 5 <- 1
|
||||
*
|
||||
* I²C <-> CS4362A (surround, center/LFE, back)
|
||||
*
|
||||
* CS4362A: AD0 <- 0
|
||||
* I²C <-> CS4362A (addr 0011000) (surround, center/LFE, back)
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -98,8 +105,8 @@
|
|||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> PCM1792A
|
||||
* <-> CS2000 (ST only)
|
||||
* I²C <-> PCM1792A (addr 1001100)
|
||||
* <-> CS2000 (addr 1001110) (ST only)
|
||||
*
|
||||
* ADC1 MCLK -> REF_CLK of CS2000 (ST only)
|
||||
*
|
||||
|
@ -109,20 +116,23 @@
|
|||
* GPIO 1 -> route HP to front panel (0) or rear jack (1)
|
||||
* GPIO 2 -> M0 of CS5381
|
||||
* GPIO 3 -> M1 of CS5381
|
||||
* GPIO 4 <- daughterboard detection
|
||||
* GPIO 5 <- daughterboard detection
|
||||
* GPIO 6 -> ?
|
||||
* GPIO 7 -> route output to speaker jacks (0) or HP (1)
|
||||
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
|
||||
*
|
||||
* PCM1792A:
|
||||
*
|
||||
* AD1,0 <- 0,0
|
||||
* SCK <- CLK_OUT of CS2000 (ST only)
|
||||
*
|
||||
* CS2000:
|
||||
*
|
||||
* AD0 <- 0
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* LINE_OUT -> input of ADC
|
||||
*
|
||||
* AUX_IN <- aux
|
||||
* MIC_IN <- mic
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
|
||||
*
|
||||
* H6 daughterboard
|
||||
|
@ -133,15 +143,39 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Xonar HDAV1.3 Slim
|
||||
* ------------------
|
||||
* Xonar Xense
|
||||
* -----------
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* GPIO 1 -> enable output
|
||||
* I²C <-> PCM1796 (addr 1001100) (front)
|
||||
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
|
||||
* <-> CS2000 (addr 1001110)
|
||||
*
|
||||
* TXD -> HDMI controller
|
||||
* RXD <- HDMI controller
|
||||
* ADC1 MCLK -> REF_CLK of CS2000
|
||||
*
|
||||
* GPI 0 <- external power present
|
||||
*
|
||||
* GPIO 0 -> enable output
|
||||
* GPIO 1 -> route HP to front panel (0) or rear jack (1)
|
||||
* GPIO 2 -> M0 of CS5381
|
||||
* GPIO 3 -> M1 of CS5381
|
||||
* GPIO 4 -> enable output
|
||||
* GPIO 5 -> enable output
|
||||
* GPIO 6 -> ?
|
||||
* GPIO 7 -> route output to HP (0) or speaker (1)
|
||||
* GPIO 8 -> route input jack to mic-in (0) or line-in (1)
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* LINE_OUT -> input of ADC
|
||||
*
|
||||
* AUX_IN <- aux
|
||||
* VIDEO_IN <- ?
|
||||
* FMIC_IN <- mic
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
|
||||
* GPO 1 -> route mic-in from input jack (0) or front panel header (1)
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
@ -150,6 +184,7 @@
|
|||
#include <sound/ac97_codec.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
|
@ -167,12 +202,14 @@
|
|||
#define GPIO_INPUT_ROUTE 0x0100
|
||||
|
||||
#define GPIO_HDAV_OUTPUT_ENABLE 0x0001
|
||||
#define GPIO_HDAV_MAGIC 0x00c0
|
||||
|
||||
#define GPIO_DB_MASK 0x0030
|
||||
#define GPIO_DB_H6 0x0000
|
||||
|
||||
#define GPIO_ST_OUTPUT_ENABLE 0x0001
|
||||
#define GPIO_ST_HP_REAR 0x0002
|
||||
#define GPIO_ST_MAGIC 0x0040
|
||||
#define GPIO_ST_HP 0x0080
|
||||
|
||||
#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */
|
||||
|
@ -186,11 +223,12 @@ struct xonar_pcm179x {
|
|||
unsigned int dacs;
|
||||
u8 pcm1796_regs[4][5];
|
||||
unsigned int current_rate;
|
||||
bool os_128;
|
||||
bool h6;
|
||||
bool hp_active;
|
||||
s8 hp_gain_offset;
|
||||
bool has_cs2000;
|
||||
u8 cs2000_fun_cfg_1;
|
||||
u8 cs2000_regs[0x1f];
|
||||
bool broken_i2c;
|
||||
};
|
||||
|
||||
struct xonar_hdav {
|
||||
|
@ -249,16 +287,14 @@ static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
|
|||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
|
||||
if (reg == CS2000_FUN_CFG_1)
|
||||
data->cs2000_fun_cfg_1 = value;
|
||||
data->cs2000_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
if (reg != CS2000_FUN_CFG_1 ||
|
||||
value != data->cs2000_fun_cfg_1)
|
||||
if (value != data->cs2000_regs[reg])
|
||||
cs2000_write(chip, reg, value);
|
||||
}
|
||||
|
||||
|
@ -268,6 +304,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
|
|||
unsigned int i;
|
||||
s8 gain_offset;
|
||||
|
||||
msleep(1);
|
||||
gain_offset = data->hp_active ? data->hp_gain_offset : 0;
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
/* set ATLD before ATL/ATR */
|
||||
|
@ -282,6 +319,7 @@ static void pcm1796_registers_init(struct oxygen *chip)
|
|||
pcm1796_write(chip, i, 20,
|
||||
data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
|
||||
pcm1796_write(chip, i, 21, 0);
|
||||
gain_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,10 +328,11 @@ static void pcm1796_init(struct oxygen *chip)
|
|||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
|
||||
PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
|
||||
PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
|
||||
data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
|
||||
PCM1796_FLT_SHARP | PCM1796_ATS_1;
|
||||
data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
|
||||
data->pcm1796_regs[0][20 - PCM1796_REG_BASE] =
|
||||
data->h6 ? PCM1796_OS_64 : PCM1796_OS_128;
|
||||
pcm1796_registers_init(chip);
|
||||
data->current_rate = 48000;
|
||||
}
|
||||
|
@ -339,18 +378,20 @@ static void xonar_hdav_init(struct oxygen *chip)
|
|||
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||
OXYGEN_2WIRE_LENGTH_8 |
|
||||
OXYGEN_2WIRE_INTERRUPT_MASK |
|
||||
OXYGEN_2WIRE_SPEED_FAST);
|
||||
OXYGEN_2WIRE_SPEED_STANDARD);
|
||||
|
||||
data->pcm179x.generic.anti_pop_delay = 100;
|
||||
data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
|
||||
data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
|
||||
data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
|
||||
data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
|
||||
data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
|
||||
data->pcm179x.dacs = chip->model.dac_channels_mixer / 2;
|
||||
data->pcm179x.h6 = chip->model.dac_channels_mixer > 2;
|
||||
|
||||
pcm1796_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_HDAV_MAGIC | GPIO_INPUT_ROUTE);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
|
||||
|
||||
xonar_init_cs53x1(chip);
|
||||
|
@ -367,7 +408,7 @@ static void xonar_st_init_i2c(struct oxygen *chip)
|
|||
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||
OXYGEN_2WIRE_LENGTH_8 |
|
||||
OXYGEN_2WIRE_INTERRUPT_MASK |
|
||||
OXYGEN_2WIRE_SPEED_FAST);
|
||||
OXYGEN_2WIRE_SPEED_STANDARD);
|
||||
}
|
||||
|
||||
static void xonar_st_init_common(struct oxygen *chip)
|
||||
|
@ -375,13 +416,14 @@ static void xonar_st_init_common(struct oxygen *chip)
|
|||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
|
||||
data->dacs = chip->model.private_data ? 4 : 1;
|
||||
data->dacs = chip->model.dac_channels_mixer / 2;
|
||||
data->hp_gain_offset = 2*-18;
|
||||
|
||||
pcm1796_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
|
||||
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR |
|
||||
GPIO_ST_MAGIC | GPIO_ST_HP);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
|
||||
|
||||
|
@ -410,9 +452,11 @@ static void cs2000_registers_init(struct oxygen *chip)
|
|||
cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
|
||||
cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
|
||||
cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
|
||||
cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
|
||||
cs2000_write(chip, CS2000_FUN_CFG_1,
|
||||
data->cs2000_regs[CS2000_FUN_CFG_1]);
|
||||
cs2000_write(chip, CS2000_FUN_CFG_2, 0);
|
||||
cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
|
||||
msleep(3); /* PLL lock delay */
|
||||
}
|
||||
|
||||
static void xonar_st_init(struct oxygen *chip)
|
||||
|
@ -420,13 +464,18 @@ static void xonar_st_init(struct oxygen *chip)
|
|||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
data->generic.anti_pop_delay = 100;
|
||||
data->h6 = chip->model.dac_channels_mixer > 2;
|
||||
data->has_cs2000 = 1;
|
||||
data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
|
||||
data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1;
|
||||
data->broken_i2c = true;
|
||||
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
|
||||
OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
|
||||
OXYGEN_RATE_48000 |
|
||||
OXYGEN_I2S_FORMAT_I2S |
|
||||
OXYGEN_I2S_MCLK(data->h6 ? MCLK_256 : MCLK_512) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
|
||||
xonar_st_init_i2c(chip);
|
||||
cs2000_registers_init(chip);
|
||||
|
@ -507,44 +556,16 @@ static void xonar_st_resume(struct oxygen *chip)
|
|||
xonar_stx_resume(chip);
|
||||
}
|
||||
|
||||
static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
if (rate <= 32000)
|
||||
return OXYGEN_I2S_MCLK_512;
|
||||
else if (rate <= 48000 && data->os_128)
|
||||
return OXYGEN_I2S_MCLK_512;
|
||||
else if (rate <= 96000)
|
||||
return OXYGEN_I2S_MCLK_256;
|
||||
else
|
||||
return OXYGEN_I2S_MCLK_128;
|
||||
}
|
||||
|
||||
static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
|
||||
unsigned int channel,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
if (channel == PCM_MULTICH)
|
||||
return mclk_from_rate(chip, params_rate(params));
|
||||
else
|
||||
return oxygen_default_i2s_mclk(chip, channel, params);
|
||||
}
|
||||
|
||||
static void update_pcm1796_oversampling(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
unsigned int i;
|
||||
u8 reg;
|
||||
|
||||
if (data->current_rate <= 32000)
|
||||
if (data->current_rate <= 48000 && !data->h6)
|
||||
reg = PCM1796_OS_128;
|
||||
else if (data->current_rate <= 48000 && data->os_128)
|
||||
reg = PCM1796_OS_128;
|
||||
else if (data->current_rate <= 96000 || data->os_128)
|
||||
reg = PCM1796_OS_64;
|
||||
else
|
||||
reg = PCM1796_OS_32;
|
||||
reg = PCM1796_OS_64;
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
pcm1796_write_cached(chip, i, 20, reg);
|
||||
}
|
||||
|
@ -554,6 +575,7 @@ static void set_pcm1796_params(struct oxygen *chip,
|
|||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
msleep(1);
|
||||
data->current_rate = params_rate(params);
|
||||
update_pcm1796_oversampling(chip);
|
||||
}
|
||||
|
@ -570,6 +592,7 @@ static void update_pcm1796_volume(struct oxygen *chip)
|
|||
+ gain_offset);
|
||||
pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
|
||||
+ gain_offset);
|
||||
gain_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,7 +602,7 @@ static void update_pcm1796_mute(struct oxygen *chip)
|
|||
unsigned int i;
|
||||
u8 value;
|
||||
|
||||
value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
|
||||
value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD;
|
||||
if (chip->dac_mute)
|
||||
value |= PCM1796_MUTE;
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
|
@ -592,45 +615,35 @@ static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
|
|||
u8 rate_mclk, reg;
|
||||
|
||||
switch (rate) {
|
||||
/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
|
||||
case 32000:
|
||||
rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
|
||||
case 64000:
|
||||
rate_mclk = OXYGEN_RATE_32000;
|
||||
break;
|
||||
case 44100:
|
||||
if (data->os_128)
|
||||
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
|
||||
else
|
||||
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
|
||||
break;
|
||||
default: /* 48000 */
|
||||
if (data->os_128)
|
||||
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
|
||||
else
|
||||
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
|
||||
break;
|
||||
case 64000:
|
||||
rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
|
||||
break;
|
||||
case 88200:
|
||||
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
|
||||
break;
|
||||
case 96000:
|
||||
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
|
||||
break;
|
||||
case 176400:
|
||||
rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
|
||||
rate_mclk = OXYGEN_RATE_44100;
|
||||
break;
|
||||
default:
|
||||
case 48000:
|
||||
case 96000:
|
||||
case 192000:
|
||||
rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
|
||||
rate_mclk = OXYGEN_RATE_48000;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate <= 96000 && (rate > 48000 || data->h6)) {
|
||||
rate_mclk |= OXYGEN_I2S_MCLK(MCLK_256);
|
||||
reg = CS2000_REF_CLK_DIV_1;
|
||||
} else {
|
||||
rate_mclk |= OXYGEN_I2S_MCLK(MCLK_512);
|
||||
reg = CS2000_REF_CLK_DIV_2;
|
||||
}
|
||||
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
|
||||
OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
|
||||
if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
|
||||
reg = CS2000_REF_CLK_DIV_1;
|
||||
else
|
||||
reg = CS2000_REF_CLK_DIV_2;
|
||||
cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
|
||||
msleep(3); /* PLL lock delay */
|
||||
}
|
||||
|
||||
static void set_st_params(struct oxygen *chip,
|
||||
|
@ -665,13 +678,7 @@ static int rolloff_info(struct snd_kcontrol *ctl,
|
|||
"Sharp Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
|
@ -719,57 +726,13 @@ static const struct snd_kcontrol_new rolloff_control = {
|
|||
.put = rolloff_put,
|
||||
};
|
||||
|
||||
static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "64x", "128x" };
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int os_128_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] = data->os_128;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int os_128_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != data->os_128;
|
||||
if (changed) {
|
||||
data->os_128 = value->value.enumerated.item[0];
|
||||
if (data->has_cs2000)
|
||||
update_cs2000_rate(chip, data->current_rate);
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
mclk_from_rate(chip, data->current_rate),
|
||||
OXYGEN_I2S_MCLK_MASK);
|
||||
update_pcm1796_oversampling(chip);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new os_128_control = {
|
||||
static const struct snd_kcontrol_new hdav_hdmi_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DAC Oversampling Playback Enum",
|
||||
.info = os_128_info,
|
||||
.get = os_128_get,
|
||||
.put = os_128_put,
|
||||
.name = "HDMI Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = xonar_gpio_bit_switch_get,
|
||||
.put = xonar_gpio_bit_switch_put,
|
||||
.private_value = GPIO_HDAV_OUTPUT_ENABLE | XONAR_GPIO_BIT_INVERT,
|
||||
};
|
||||
|
||||
static int st_output_switch_info(struct snd_kcontrol *ctl,
|
||||
|
@ -779,13 +742,7 @@ static int st_output_switch_info(struct snd_kcontrol *ctl,
|
|||
"Speakers", "Headphones", "FP Headphones"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 3;
|
||||
if (info->value.enumerated.item >= 3)
|
||||
info->value.enumerated.item = 2;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int st_output_switch_get(struct snd_kcontrol *ctl,
|
||||
|
@ -840,13 +797,7 @@ static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
|
|||
"< 64 ohms", "64-300 ohms", "300-600 ohms"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 3;
|
||||
if (info->value.enumerated.item > 2)
|
||||
info->value.enumerated.item = 2;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
|
||||
|
@ -928,16 +879,25 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int xonar_st_h6_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strncmp(template->name, "Master Playback ", 16))
|
||||
/* no volume/mute, as I²C to the third DAC does not work */
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_pcm1796_controls(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
int err;
|
||||
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
|
||||
if (!data->broken_i2c) {
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&rolloff_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -956,7 +916,15 @@ static int xonar_d2_mixer_init(struct oxygen *chip)
|
|||
|
||||
static int xonar_hdav_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
return add_pcm1796_controls(chip);
|
||||
int err;
|
||||
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&hdav_hdmi_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_pcm1796_controls(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xonar_st_mixer_init(struct oxygen *chip)
|
||||
|
@ -976,6 +944,45 @@ static int xonar_st_mixer_init(struct oxygen *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dump_pcm1796_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
unsigned int dac, i;
|
||||
|
||||
for (dac = 0; dac < data->dacs; ++dac) {
|
||||
snd_iprintf(buffer, "\nPCM1796 %u:", dac + 1);
|
||||
for (i = 0; i < 5; ++i)
|
||||
snd_iprintf(buffer, " %02x",
|
||||
data->pcm1796_regs[dac][i]);
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_cs2000_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_pcm179x *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
if (data->has_cs2000) {
|
||||
snd_iprintf(buffer, "\nCS2000:\n00: ");
|
||||
for (i = 1; i < 0x10; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
|
||||
snd_iprintf(buffer, "\n10:");
|
||||
for (i = 0x10; i < 0x1f; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs2000_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_st_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
dump_pcm1796_registers(chip, buffer);
|
||||
dump_cs2000_registers(chip, buffer);
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_xonar_d2 = {
|
||||
.longname = "Asus Virtuoso 200",
|
||||
.chip = "AV200",
|
||||
|
@ -985,11 +992,11 @@ static const struct oxygen_model model_xonar_d2 = {
|
|||
.cleanup = xonar_d2_cleanup,
|
||||
.suspend = xonar_d2_suspend,
|
||||
.resume = xonar_d2_resume,
|
||||
.get_i2s_mclk = get_pcm1796_i2s_mclk,
|
||||
.set_dac_params = set_pcm1796_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_pcm1796_volume,
|
||||
.update_dac_mute = update_pcm1796_mute,
|
||||
.dump_registers = dump_pcm1796_registers,
|
||||
.dac_tlv = pcm1796_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_pcm179x),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
|
@ -999,13 +1006,16 @@ static const struct oxygen_model model_xonar_d2 = {
|
|||
MIDI_OUTPUT |
|
||||
MIDI_INPUT |
|
||||
AC97_CD_INPUT,
|
||||
.dac_channels = 8,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 255 - 2*60,
|
||||
.dac_volume_max = 255,
|
||||
.misc_flags = OXYGEN_MISC_MIDI,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI |
|
||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
|
@ -1018,25 +1028,28 @@ static const struct oxygen_model model_xonar_hdav = {
|
|||
.suspend = xonar_hdav_suspend,
|
||||
.resume = xonar_hdav_resume,
|
||||
.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
|
||||
.get_i2s_mclk = get_pcm1796_i2s_mclk,
|
||||
.set_dac_params = set_hdav_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_pcm1796_volume,
|
||||
.update_dac_mute = update_pcm1796_mute,
|
||||
.uart_input = xonar_hdmi_uart_input,
|
||||
.ac97_switch = xonar_line_mic_ac97_switch,
|
||||
.dump_registers = dump_pcm1796_registers,
|
||||
.dac_tlv = pcm1796_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_hdav),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF,
|
||||
.dac_channels = 8,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 2,
|
||||
.dac_volume_min = 255 - 2*60,
|
||||
.dac_volume_max = 255,
|
||||
.misc_flags = OXYGEN_MISC_MIDI,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
|
@ -1048,22 +1061,26 @@ static const struct oxygen_model model_xonar_st = {
|
|||
.cleanup = xonar_st_cleanup,
|
||||
.suspend = xonar_st_suspend,
|
||||
.resume = xonar_st_resume,
|
||||
.get_i2s_mclk = get_pcm1796_i2s_mclk,
|
||||
.set_dac_params = set_st_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_pcm1796_volume,
|
||||
.update_dac_mute = update_pcm1796_mute,
|
||||
.ac97_switch = xonar_line_mic_ac97_switch,
|
||||
.dump_registers = dump_st_registers,
|
||||
.dac_tlv = pcm1796_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_pcm179x),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2,
|
||||
.dac_channels = 2,
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
AC97_FMIC_SWITCH,
|
||||
.dac_channels_pcm = 2,
|
||||
.dac_channels_mixer = 2,
|
||||
.dac_volume_min = 255 - 2*60,
|
||||
.dac_volume_max = 255,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.dac_mclks = OXYGEN_MCLKS(512, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_I2S,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
|
@ -1089,7 +1106,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
|
|||
break;
|
||||
case GPIO_DB_H6:
|
||||
chip->model.shortname = "Xonar HDAV1.3+H6";
|
||||
chip->model.private_data = 1;
|
||||
chip->model.dac_channels_mixer = 8;
|
||||
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -1102,8 +1120,10 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
|
|||
break;
|
||||
case GPIO_DB_H6:
|
||||
chip->model.shortname = "Xonar ST+H6";
|
||||
chip->model.dac_channels = 8;
|
||||
chip->model.private_data = 1;
|
||||
chip->model.control_filter = xonar_st_h6_control_filter;
|
||||
chip->model.dac_channels_pcm = 8;
|
||||
chip->model.dac_channels_mixer = 8;
|
||||
chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -1114,9 +1134,6 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
|
|||
chip->model.resume = xonar_stx_resume;
|
||||
chip->model.set_dac_params = set_pcm1796_params;
|
||||
break;
|
||||
case 0x835e:
|
||||
snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
|
||||
return -ENODEV;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
*
|
||||
|
@ -30,7 +30,7 @@
|
|||
* GPIO 7 -> enable output to front L/R speaker channels
|
||||
* GPIO 8 -> enable output to other speaker channels and front panel headphone
|
||||
*
|
||||
* WM8766:
|
||||
* WM8776:
|
||||
*
|
||||
* input 1 <- line
|
||||
* input 2 <- mic
|
||||
|
@ -38,10 +38,32 @@
|
|||
* input 4 <- aux
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xonar HDAV1.3 Slim
|
||||
* ------------------
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> WM8776 (addr 0011010)
|
||||
*
|
||||
* GPIO 0 -> disable HDMI output
|
||||
* GPIO 1 -> enable HP output
|
||||
* GPIO 6 -> firmware EEPROM I²C clock
|
||||
* GPIO 7 <-> firmware EEPROM I²C data
|
||||
*
|
||||
* UART <-> HDMI controller
|
||||
*
|
||||
* WM8776:
|
||||
*
|
||||
* input 1 <- mic
|
||||
* input 2 <- aux
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
@ -55,6 +77,13 @@
|
|||
#define GPIO_DS_OUTPUT_FRONTLR 0x0080
|
||||
#define GPIO_DS_OUTPUT_ENABLE 0x0100
|
||||
|
||||
#define GPIO_SLIM_HDMI_DISABLE 0x0001
|
||||
#define GPIO_SLIM_OUTPUT_ENABLE 0x0002
|
||||
#define GPIO_SLIM_FIRMWARE_CLK 0x0040
|
||||
#define GPIO_SLIM_FIRMWARE_DATA 0x0080
|
||||
|
||||
#define I2C_DEVICE_WM8776 0x34 /* 001101, 0, /W=0 */
|
||||
|
||||
#define LC_CONTROL_LIMITER 0x40000000
|
||||
#define LC_CONTROL_ALC 0x20000000
|
||||
|
||||
|
@ -66,19 +95,37 @@ struct xonar_wm87x6 {
|
|||
struct snd_kcontrol *mic_adcmux_control;
|
||||
struct snd_kcontrol *lc_controls[13];
|
||||
struct snd_jack *hp_jack;
|
||||
struct xonar_hdmi hdmi;
|
||||
};
|
||||
|
||||
static void wm8776_write(struct oxygen *chip,
|
||||
static void wm8776_write_spi(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CLOCK_160 |
|
||||
(1 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
|
||||
(reg << 9) | value);
|
||||
}
|
||||
|
||||
static void wm8776_write_i2c(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_WM8776,
|
||||
(reg << 1) | (value >> 8), value);
|
||||
}
|
||||
|
||||
static void wm8776_write(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
||||
if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
|
||||
OXYGEN_FUNCTION_SPI)
|
||||
wm8776_write_spi(chip, reg, value);
|
||||
else
|
||||
wm8776_write_i2c(chip, reg, value);
|
||||
if (reg < ARRAY_SIZE(data->wm8776_regs)) {
|
||||
if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
|
||||
value &= ~WM8776_UPDATE;
|
||||
|
@ -245,17 +292,50 @@ static void xonar_ds_init(struct oxygen *chip)
|
|||
snd_component_add(chip->card, "WM8766");
|
||||
}
|
||||
|
||||
static void xonar_hdav_slim_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
||||
data->generic.anti_pop_delay = 300;
|
||||
data->generic.output_enable_bit = GPIO_SLIM_OUTPUT_ENABLE;
|
||||
|
||||
wm8776_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_SLIM_HDMI_DISABLE |
|
||||
GPIO_SLIM_FIRMWARE_CLK |
|
||||
GPIO_SLIM_FIRMWARE_DATA);
|
||||
|
||||
xonar_hdmi_init(chip, &data->hdmi);
|
||||
xonar_enable_output(chip);
|
||||
|
||||
snd_component_add(chip->card, "WM8776");
|
||||
}
|
||||
|
||||
static void xonar_ds_cleanup(struct oxygen *chip)
|
||||
{
|
||||
xonar_disable_output(chip);
|
||||
wm8776_write(chip, WM8776_RESET, 0);
|
||||
}
|
||||
|
||||
static void xonar_hdav_slim_cleanup(struct oxygen *chip)
|
||||
{
|
||||
xonar_hdmi_cleanup(chip);
|
||||
xonar_disable_output(chip);
|
||||
wm8776_write(chip, WM8776_RESET, 0);
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
static void xonar_ds_suspend(struct oxygen *chip)
|
||||
{
|
||||
xonar_ds_cleanup(chip);
|
||||
}
|
||||
|
||||
static void xonar_hdav_slim_suspend(struct oxygen *chip)
|
||||
{
|
||||
xonar_hdav_slim_cleanup(chip);
|
||||
}
|
||||
|
||||
static void xonar_ds_resume(struct oxygen *chip)
|
||||
{
|
||||
wm8776_registers_init(chip);
|
||||
|
@ -264,6 +344,15 @@ static void xonar_ds_resume(struct oxygen *chip)
|
|||
xonar_ds_handle_hp_jack(chip);
|
||||
}
|
||||
|
||||
static void xonar_hdav_slim_resume(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
||||
wm8776_registers_init(chip);
|
||||
xonar_hdmi_resume(chip, &data->hdmi);
|
||||
xonar_enable_output(chip);
|
||||
}
|
||||
|
||||
static void wm8776_adc_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware)
|
||||
{
|
||||
|
@ -278,6 +367,13 @@ static void wm8776_adc_hardware_filter(unsigned int channel,
|
|||
}
|
||||
}
|
||||
|
||||
static void xonar_hdav_slim_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware)
|
||||
{
|
||||
wm8776_adc_hardware_filter(channel, hardware);
|
||||
xonar_hdmi_pcm_hardware_filter(channel, hardware);
|
||||
}
|
||||
|
||||
static void set_wm87x6_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -294,6 +390,14 @@ static void set_wm8776_adc_params(struct oxygen *chip,
|
|||
wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
|
||||
}
|
||||
|
||||
static void set_hdav_slim_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
||||
xonar_set_hdmi_params(chip, &data->hdmi, params);
|
||||
}
|
||||
|
||||
static void update_wm8776_volume(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
@ -473,11 +577,6 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
|
|||
const char *const *names;
|
||||
|
||||
max = (ctl->private_value >> 12) & 0xf;
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = max + 1;
|
||||
if (info->value.enumerated.item > max)
|
||||
info->value.enumerated.item = max;
|
||||
switch ((ctl->private_value >> 24) & 0x1f) {
|
||||
case WM8776_ALCCTRL2:
|
||||
names = hld;
|
||||
|
@ -501,8 +600,7 @@ static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
|
|||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, max + 1, names);
|
||||
}
|
||||
|
||||
static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
|
||||
|
@ -759,13 +857,8 @@ static int wm8776_level_control_info(struct snd_kcontrol *ctl,
|
|||
static const char *const names[3] = {
|
||||
"None", "Peak Limiter", "Automatic Level Control"
|
||||
};
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 3;
|
||||
if (info->value.enumerated.item >= 3)
|
||||
info->value.enumerated.item = 2;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int wm8776_level_control_get(struct snd_kcontrol *ctl,
|
||||
|
@ -851,13 +944,7 @@ static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
|||
"None", "High-pass Filter"
|
||||
};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item >= 2)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
|
@ -985,6 +1072,53 @@ static const struct snd_kcontrol_new ds_controls[] = {
|
|||
.private_value = 0,
|
||||
},
|
||||
};
|
||||
static const struct snd_kcontrol_new hdav_slim_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HDMI Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = xonar_gpio_bit_switch_get,
|
||||
.put = xonar_gpio_bit_switch_put,
|
||||
.private_value = GPIO_SLIM_HDMI_DISABLE | XONAR_GPIO_BIT_INVERT,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone Playback Volume",
|
||||
.info = wm8776_hp_vol_info,
|
||||
.get = wm8776_hp_vol_get,
|
||||
.put = wm8776_hp_vol_put,
|
||||
.tlv = { .p = wm8776_hp_db_scale },
|
||||
},
|
||||
WM8776_BIT_SWITCH("Headphone Playback Switch",
|
||||
WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Input Capture Volume",
|
||||
.info = wm8776_input_vol_info,
|
||||
.get = wm8776_input_vol_get,
|
||||
.put = wm8776_input_vol_put,
|
||||
.tlv = { .p = wm8776_adc_db_scale },
|
||||
},
|
||||
WM8776_BIT_SWITCH("Mic Capture Switch",
|
||||
WM8776_ADCMUX, 1 << 0, 0, 0),
|
||||
WM8776_BIT_SWITCH("Aux Capture Switch",
|
||||
WM8776_ADCMUX, 1 << 1, 0, 0),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Level Control Capture Enum",
|
||||
.info = wm8776_level_control_info,
|
||||
.get = wm8776_level_control_get,
|
||||
.put = wm8776_level_control_put,
|
||||
.private_value = 0,
|
||||
},
|
||||
};
|
||||
static const struct snd_kcontrol_new lc_controls[] = {
|
||||
WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
|
||||
WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
|
||||
|
@ -1028,6 +1162,26 @@ static const struct snd_kcontrol_new lc_controls[] = {
|
|||
LC_CONTROL_ALC, wm8776_ngth_db_scale),
|
||||
};
|
||||
|
||||
static int add_lc_controls(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
unsigned int i;
|
||||
struct snd_kcontrol *ctl;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
|
||||
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
|
||||
ctl = snd_ctl_new1(&lc_controls[i], chip);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
err = snd_ctl_add(chip->card, ctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data->lc_controls[i] = ctl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xonar_ds_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
|
@ -1049,17 +1203,54 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
|
|||
}
|
||||
if (!data->line_adcmux_control || !data->mic_adcmux_control)
|
||||
return -ENXIO;
|
||||
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
|
||||
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
|
||||
ctl = snd_ctl_new1(&lc_controls[i], chip);
|
||||
|
||||
return add_lc_controls(chip);
|
||||
}
|
||||
|
||||
static int xonar_hdav_slim_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
struct snd_kcontrol *ctl;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hdav_slim_controls); ++i) {
|
||||
ctl = snd_ctl_new1(&hdav_slim_controls[i], chip);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
err = snd_ctl_add(chip->card, ctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data->lc_controls[i] = ctl;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return add_lc_controls(chip);
|
||||
}
|
||||
|
||||
static void dump_wm8776_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nWM8776:\n00:");
|
||||
for (i = 0; i < 0x10; ++i)
|
||||
snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
|
||||
snd_iprintf(buffer, "\n10:");
|
||||
for (i = 0x10; i < 0x17; ++i)
|
||||
snd_iprintf(buffer, " %03x", data->wm8776_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_wm87x6_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_wm87x6 *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
dump_wm8776_registers(chip, buffer);
|
||||
snd_iprintf(buffer, "\nWM8766:\n00:");
|
||||
for (i = 0; i < 0x10; ++i)
|
||||
snd_iprintf(buffer, " %03x", data->wm8766_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_xonar_ds = {
|
||||
|
@ -1072,22 +1263,57 @@ static const struct oxygen_model model_xonar_ds = {
|
|||
.suspend = xonar_ds_suspend,
|
||||
.resume = xonar_ds_resume,
|
||||
.pcm_hardware_filter = wm8776_adc_hardware_filter,
|
||||
.get_i2s_mclk = oxygen_default_i2s_mclk,
|
||||
.set_dac_params = set_wm87x6_dac_params,
|
||||
.set_adc_params = set_wm8776_adc_params,
|
||||
.update_dac_volume = update_wm87x6_volume,
|
||||
.update_dac_mute = update_wm87x6_mute,
|
||||
.update_center_lfe_mix = update_wm8766_center_lfe_mix,
|
||||
.gpio_changed = xonar_ds_gpio_changed,
|
||||
.dump_registers = dump_wm87x6_registers,
|
||||
.dac_tlv = wm87x6_dac_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_wm87x6),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_1,
|
||||
.dac_channels = 8,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 255 - 2*60,
|
||||
.dac_volume_max = 255,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
static const struct oxygen_model model_xonar_hdav_slim = {
|
||||
.shortname = "Xonar HDAV1.3 Slim",
|
||||
.longname = "Asus Virtuoso 200",
|
||||
.chip = "AV200",
|
||||
.init = xonar_hdav_slim_init,
|
||||
.mixer_init = xonar_hdav_slim_mixer_init,
|
||||
.cleanup = xonar_hdav_slim_cleanup,
|
||||
.suspend = xonar_hdav_slim_suspend,
|
||||
.resume = xonar_hdav_slim_resume,
|
||||
.pcm_hardware_filter = xonar_hdav_slim_hardware_filter,
|
||||
.set_dac_params = set_hdav_slim_dac_params,
|
||||
.set_adc_params = set_wm8776_adc_params,
|
||||
.update_dac_volume = update_wm8776_volume,
|
||||
.update_dac_mute = update_wm8776_mute,
|
||||
.uart_input = xonar_hdmi_uart_input,
|
||||
.dump_registers = dump_wm8776_registers,
|
||||
.dac_tlv = wm87x6_dac_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_wm87x6),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_1,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 2,
|
||||
.dac_volume_min = 255 - 2*60,
|
||||
.dac_volume_max = 255,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
@ -1099,6 +1325,9 @@ int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
|
|||
case 0x838e:
|
||||
chip->model = model_xonar_ds;
|
||||
break;
|
||||
case 0x835e:
|
||||
chip->model = model_xonar_hdav_slim;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP},"
|
|||
"{RME HDSP-9652},"
|
||||
"{RME HDSP-9632}}");
|
||||
#ifdef HDSP_FW_LOADER
|
||||
MODULE_FIRMWARE("rpm_firmware.bin");
|
||||
MODULE_FIRMWARE("multiface_firmware.bin");
|
||||
MODULE_FIRMWARE("multiface_firmware_rev11.bin");
|
||||
MODULE_FIRMWARE("digiface_firmware.bin");
|
||||
|
@ -81,6 +82,7 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
|
|||
#define H9632_SS_CHANNELS 12
|
||||
#define H9632_DS_CHANNELS 8
|
||||
#define H9632_QS_CHANNELS 4
|
||||
#define RPM_CHANNELS 6
|
||||
|
||||
/* Write registers. These are defined as byte-offsets from the iobase value.
|
||||
*/
|
||||
|
@ -191,6 +193,25 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
|
|||
#define HDSP_PhoneGain1 (1<<30)
|
||||
#define HDSP_QuadSpeed (1<<31)
|
||||
|
||||
/* RPM uses some of the registers for special purposes */
|
||||
#define HDSP_RPM_Inp12 0x04A00
|
||||
#define HDSP_RPM_Inp12_Phon_6dB 0x00800 /* Dolby */
|
||||
#define HDSP_RPM_Inp12_Phon_0dB 0x00000 /* .. */
|
||||
#define HDSP_RPM_Inp12_Phon_n6dB 0x04000 /* inp_0 */
|
||||
#define HDSP_RPM_Inp12_Line_0dB 0x04200 /* Dolby+PRO */
|
||||
#define HDSP_RPM_Inp12_Line_n6dB 0x00200 /* PRO */
|
||||
|
||||
#define HDSP_RPM_Inp34 0x32000
|
||||
#define HDSP_RPM_Inp34_Phon_6dB 0x20000 /* SyncRef1 */
|
||||
#define HDSP_RPM_Inp34_Phon_0dB 0x00000 /* .. */
|
||||
#define HDSP_RPM_Inp34_Phon_n6dB 0x02000 /* SyncRef2 */
|
||||
#define HDSP_RPM_Inp34_Line_0dB 0x30000 /* SyncRef1+SyncRef0 */
|
||||
#define HDSP_RPM_Inp34_Line_n6dB 0x10000 /* SyncRef0 */
|
||||
|
||||
#define HDSP_RPM_Bypass 0x01000
|
||||
|
||||
#define HDSP_RPM_Disconnect 0x00001
|
||||
|
||||
#define HDSP_ADGainMask (HDSP_ADGain0|HDSP_ADGain1)
|
||||
#define HDSP_ADGainMinus10dBV HDSP_ADGainMask
|
||||
#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
|
||||
|
@ -450,7 +471,7 @@ struct hdsp {
|
|||
u32 creg_spdif;
|
||||
u32 creg_spdif_stream;
|
||||
int clock_source_locked;
|
||||
char *card_name; /* digiface/multiface */
|
||||
char *card_name; /* digiface/multiface/rpm */
|
||||
enum HDSP_IO_Type io_type; /* ditto, but for code use */
|
||||
unsigned short firmware_rev;
|
||||
unsigned short state; /* stores state bits */
|
||||
|
@ -612,6 +633,7 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out)
|
|||
switch (hdsp->io_type) {
|
||||
case Multiface:
|
||||
case Digiface:
|
||||
case RPM:
|
||||
default:
|
||||
if (hdsp->firmware_rev == 0xa)
|
||||
return (64 * out) + (32 + (in));
|
||||
|
@ -629,6 +651,7 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out)
|
|||
switch (hdsp->io_type) {
|
||||
case Multiface:
|
||||
case Digiface:
|
||||
case RPM:
|
||||
default:
|
||||
if (hdsp->firmware_rev == 0xa)
|
||||
return (64 * out) + in;
|
||||
|
@ -655,7 +678,7 @@ static int hdsp_check_for_iobox (struct hdsp *hdsp)
|
|||
{
|
||||
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
|
||||
if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
|
||||
snd_printk ("Hammerfall-DSP: no Digiface or Multiface connected!\n");
|
||||
snd_printk("Hammerfall-DSP: no IO box connected!\n");
|
||||
hdsp->state &= ~HDSP_FirmwareLoaded;
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -680,7 +703,7 @@ static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
|
|||
}
|
||||
}
|
||||
|
||||
snd_printk("Hammerfall-DSP: no Digiface or Multiface connected!\n");
|
||||
snd_printk("Hammerfall-DSP: no IO box connected!\n");
|
||||
hdsp->state &= ~HDSP_FirmwareLoaded;
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -752,17 +775,21 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
|
|||
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
|
||||
hdsp_write (hdsp, HDSP_fifoData, 0);
|
||||
|
||||
if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT)) {
|
||||
if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) {
|
||||
hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
|
||||
hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
|
||||
if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT))
|
||||
hdsp->io_type = RPM;
|
||||
else
|
||||
hdsp->io_type = Multiface;
|
||||
hdsp_write (hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
|
||||
hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
|
||||
hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT);
|
||||
} else {
|
||||
hdsp->io_type = Digiface;
|
||||
}
|
||||
} else {
|
||||
/* firmware was already loaded, get iobox type */
|
||||
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
|
||||
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
|
||||
hdsp->io_type = RPM;
|
||||
else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
|
||||
hdsp->io_type = Multiface;
|
||||
else
|
||||
hdsp->io_type = Digiface;
|
||||
|
@ -1184,6 +1211,7 @@ static int hdsp_set_rate(struct hdsp *hdsp, int rate, int called_internally)
|
|||
hdsp->channel_map = channel_map_ds;
|
||||
} else {
|
||||
switch (hdsp->io_type) {
|
||||
case RPM:
|
||||
case Multiface:
|
||||
hdsp->channel_map = channel_map_mf_ss;
|
||||
break;
|
||||
|
@ -3231,6 +3259,318 @@ HDSP_PRECISE_POINTER("Precise Pointer", 0),
|
|||
HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
|
||||
};
|
||||
|
||||
|
||||
static int hdsp_rpm_input12(struct hdsp *hdsp)
|
||||
{
|
||||
switch (hdsp->control_register & HDSP_RPM_Inp12) {
|
||||
case HDSP_RPM_Inp12_Phon_6dB:
|
||||
return 0;
|
||||
case HDSP_RPM_Inp12_Phon_n6dB:
|
||||
return 2;
|
||||
case HDSP_RPM_Inp12_Line_0dB:
|
||||
return 3;
|
||||
case HDSP_RPM_Inp12_Line_n6dB:
|
||||
return 4;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_get_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = hdsp_rpm_input12(hdsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hdsp_set_rpm_input12(struct hdsp *hdsp, int mode)
|
||||
{
|
||||
hdsp->control_register &= ~HDSP_RPM_Inp12;
|
||||
switch (mode) {
|
||||
case 0:
|
||||
hdsp->control_register |= HDSP_RPM_Inp12_Phon_6dB;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
hdsp->control_register |= HDSP_RPM_Inp12_Phon_n6dB;
|
||||
break;
|
||||
case 3:
|
||||
hdsp->control_register |= HDSP_RPM_Inp12_Line_0dB;
|
||||
break;
|
||||
case 4:
|
||||
hdsp->control_register |= HDSP_RPM_Inp12_Line_n6dB;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
int change;
|
||||
int val;
|
||||
|
||||
if (!snd_hdsp_use_is_exclusive(hdsp))
|
||||
return -EBUSY;
|
||||
val = ucontrol->value.enumerated.item[0];
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
if (val > 4)
|
||||
val = 4;
|
||||
spin_lock_irq(&hdsp->lock);
|
||||
if (val != hdsp_rpm_input12(hdsp))
|
||||
change = (hdsp_set_rpm_input12(hdsp, val) == 0) ? 1 : 0;
|
||||
else
|
||||
change = 0;
|
||||
spin_unlock_irq(&hdsp->lock);
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[] = {"Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 5;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hdsp_rpm_input34(struct hdsp *hdsp)
|
||||
{
|
||||
switch (hdsp->control_register & HDSP_RPM_Inp34) {
|
||||
case HDSP_RPM_Inp34_Phon_6dB:
|
||||
return 0;
|
||||
case HDSP_RPM_Inp34_Phon_n6dB:
|
||||
return 2;
|
||||
case HDSP_RPM_Inp34_Line_0dB:
|
||||
return 3;
|
||||
case HDSP_RPM_Inp34_Line_n6dB:
|
||||
return 4;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_get_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = hdsp_rpm_input34(hdsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hdsp_set_rpm_input34(struct hdsp *hdsp, int mode)
|
||||
{
|
||||
hdsp->control_register &= ~HDSP_RPM_Inp34;
|
||||
switch (mode) {
|
||||
case 0:
|
||||
hdsp->control_register |= HDSP_RPM_Inp34_Phon_6dB;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
hdsp->control_register |= HDSP_RPM_Inp34_Phon_n6dB;
|
||||
break;
|
||||
case 3:
|
||||
hdsp->control_register |= HDSP_RPM_Inp34_Line_0dB;
|
||||
break;
|
||||
case 4:
|
||||
hdsp->control_register |= HDSP_RPM_Inp34_Line_n6dB;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_put_rpm_input34(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
int change;
|
||||
int val;
|
||||
|
||||
if (!snd_hdsp_use_is_exclusive(hdsp))
|
||||
return -EBUSY;
|
||||
val = ucontrol->value.enumerated.item[0];
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
if (val > 4)
|
||||
val = 4;
|
||||
spin_lock_irq(&hdsp->lock);
|
||||
if (val != hdsp_rpm_input34(hdsp))
|
||||
change = (hdsp_set_rpm_input34(hdsp, val) == 0) ? 1 : 0;
|
||||
else
|
||||
change = 0;
|
||||
spin_unlock_irq(&hdsp->lock);
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
/* RPM Bypass switch */
|
||||
static int hdsp_rpm_bypass(struct hdsp *hdsp)
|
||||
{
|
||||
return (hdsp->control_register & HDSP_RPM_Bypass) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_get_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] = hdsp_rpm_bypass(hdsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hdsp_set_rpm_bypass(struct hdsp *hdsp, int on)
|
||||
{
|
||||
if (on)
|
||||
hdsp->control_register |= HDSP_RPM_Bypass;
|
||||
else
|
||||
hdsp->control_register &= ~HDSP_RPM_Bypass;
|
||||
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
int change;
|
||||
unsigned int val;
|
||||
|
||||
if (!snd_hdsp_use_is_exclusive(hdsp))
|
||||
return -EBUSY;
|
||||
val = ucontrol->value.integer.value[0] & 1;
|
||||
spin_lock_irq(&hdsp->lock);
|
||||
change = (int)val != hdsp_rpm_bypass(hdsp);
|
||||
hdsp_set_rpm_bypass(hdsp, val);
|
||||
spin_unlock_irq(&hdsp->lock);
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[] = {"On", "Off"};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 2;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* RPM Disconnect switch */
|
||||
static int hdsp_rpm_disconnect(struct hdsp *hdsp)
|
||||
{
|
||||
return (hdsp->control_register & HDSP_RPM_Disconnect) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_get_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.integer.value[0] = hdsp_rpm_disconnect(hdsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hdsp_set_rpm_disconnect(struct hdsp *hdsp, int on)
|
||||
{
|
||||
if (on)
|
||||
hdsp->control_register |= HDSP_RPM_Disconnect;
|
||||
else
|
||||
hdsp->control_register &= ~HDSP_RPM_Disconnect;
|
||||
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
|
||||
int change;
|
||||
unsigned int val;
|
||||
|
||||
if (!snd_hdsp_use_is_exclusive(hdsp))
|
||||
return -EBUSY;
|
||||
val = ucontrol->value.integer.value[0] & 1;
|
||||
spin_lock_irq(&hdsp->lock);
|
||||
change = (int)val != hdsp_rpm_disconnect(hdsp);
|
||||
hdsp_set_rpm_disconnect(hdsp, val);
|
||||
spin_unlock_irq(&hdsp->lock);
|
||||
return change;
|
||||
}
|
||||
|
||||
static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[] = {"On", "Off"};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 2;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "RPM Bypass",
|
||||
.get = snd_hdsp_get_rpm_bypass,
|
||||
.put = snd_hdsp_put_rpm_bypass,
|
||||
.info = snd_hdsp_info_rpm_bypass
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "RPM Disconnect",
|
||||
.get = snd_hdsp_get_rpm_disconnect,
|
||||
.put = snd_hdsp_put_rpm_disconnect,
|
||||
.info = snd_hdsp_info_rpm_disconnect
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Input 1/2",
|
||||
.get = snd_hdsp_get_rpm_input12,
|
||||
.put = snd_hdsp_put_rpm_input12,
|
||||
.info = snd_hdsp_info_rpm_input
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Input 3/4",
|
||||
.get = snd_hdsp_get_rpm_input34,
|
||||
.put = snd_hdsp_put_rpm_input34,
|
||||
.info = snd_hdsp_info_rpm_input
|
||||
},
|
||||
HDSP_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
|
||||
HDSP_MIXER("Mixer", 0)
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
|
||||
static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
|
||||
|
||||
|
@ -3240,6 +3580,16 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
|
|||
int err;
|
||||
struct snd_kcontrol *kctl;
|
||||
|
||||
if (hdsp->io_type == RPM) {
|
||||
/* RPM Bypass, Disconnect and Input switches */
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_rpm_controls); idx++) {
|
||||
err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_rpm_controls[idx], hdsp));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) {
|
||||
if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0)
|
||||
return err;
|
||||
|
@ -3459,6 +3809,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
|
||||
snd_iprintf(buffer, "\n");
|
||||
|
||||
if (hdsp->io_type != RPM) {
|
||||
switch (hdsp_spdif_in(hdsp)) {
|
||||
case HDSP_SPDIFIN_OPTICAL:
|
||||
snd_iprintf(buffer, "IEC958 input: Optical\n");
|
||||
|
@ -3476,7 +3827,59 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
snd_iprintf(buffer, "IEC958 input: ???\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (RPM == hdsp->io_type) {
|
||||
if (hdsp->control_register & HDSP_RPM_Bypass)
|
||||
snd_iprintf(buffer, "RPM Bypass: disabled\n");
|
||||
else
|
||||
snd_iprintf(buffer, "RPM Bypass: enabled\n");
|
||||
if (hdsp->control_register & HDSP_RPM_Disconnect)
|
||||
snd_iprintf(buffer, "RPM disconnected\n");
|
||||
else
|
||||
snd_iprintf(buffer, "RPM connected\n");
|
||||
|
||||
switch (hdsp->control_register & HDSP_RPM_Inp12) {
|
||||
case HDSP_RPM_Inp12_Phon_6dB:
|
||||
snd_iprintf(buffer, "Input 1/2: Phono, 6dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp12_Phon_0dB:
|
||||
snd_iprintf(buffer, "Input 1/2: Phono, 0dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp12_Phon_n6dB:
|
||||
snd_iprintf(buffer, "Input 1/2: Phono, -6dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp12_Line_0dB:
|
||||
snd_iprintf(buffer, "Input 1/2: Line, 0dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp12_Line_n6dB:
|
||||
snd_iprintf(buffer, "Input 1/2: Line, -6dB\n");
|
||||
break;
|
||||
default:
|
||||
snd_iprintf(buffer, "Input 1/2: ???\n");
|
||||
}
|
||||
|
||||
switch (hdsp->control_register & HDSP_RPM_Inp34) {
|
||||
case HDSP_RPM_Inp34_Phon_6dB:
|
||||
snd_iprintf(buffer, "Input 3/4: Phono, 6dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp34_Phon_0dB:
|
||||
snd_iprintf(buffer, "Input 3/4: Phono, 0dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp34_Phon_n6dB:
|
||||
snd_iprintf(buffer, "Input 3/4: Phono, -6dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp34_Line_0dB:
|
||||
snd_iprintf(buffer, "Input 3/4: Line, 0dB\n");
|
||||
break;
|
||||
case HDSP_RPM_Inp34_Line_n6dB:
|
||||
snd_iprintf(buffer, "Input 3/4: Line, -6dB\n");
|
||||
break;
|
||||
default:
|
||||
snd_iprintf(buffer, "Input 3/4: ???\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (hdsp->control_register & HDSP_SPDIFOpticalOut)
|
||||
snd_iprintf(buffer, "IEC958 output: Coaxial & ADAT1\n");
|
||||
else
|
||||
|
@ -3496,11 +3899,12 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
snd_iprintf(buffer, "IEC958 NonAudio: on\n");
|
||||
else
|
||||
snd_iprintf(buffer, "IEC958 NonAudio: off\n");
|
||||
if ((x = hdsp_spdif_sample_rate (hdsp)) != 0)
|
||||
snd_iprintf (buffer, "IEC958 sample rate: %d\n", x);
|
||||
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, "IEC958 sample rate: Error flag set\n");
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
|
||||
/* Sync Check */
|
||||
|
@ -3765,7 +4169,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
|
|||
snd_hdsp_midi_input_read (&hdsp->midi[0]);
|
||||
}
|
||||
}
|
||||
if (hdsp->io_type != Multiface && hdsp->io_type != H9632 && midi1 && midi1status) {
|
||||
if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) {
|
||||
if (hdsp->use_midi_tasklet) {
|
||||
/* we disable interrupts for this input until processing is done */
|
||||
hdsp->control_register &= ~HDSP_Midi1InterruptEnable;
|
||||
|
@ -4093,7 +4497,7 @@ static struct snd_pcm_hardware snd_hdsp_playback_subinfo =
|
|||
SNDRV_PCM_RATE_96000),
|
||||
.rate_min = 32000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 14,
|
||||
.channels_min = 6,
|
||||
.channels_max = HDSP_MAX_CHANNELS,
|
||||
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
|
||||
.period_bytes_min = (64 * 4) * 10,
|
||||
|
@ -4122,7 +4526,7 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo =
|
|||
SNDRV_PCM_RATE_96000),
|
||||
.rate_min = 32000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 14,
|
||||
.channels_min = 5,
|
||||
.channels_max = HDSP_MAX_CHANNELS,
|
||||
.buffer_bytes_max = HDSP_CHANNEL_BUFFER_BYTES * HDSP_MAX_CHANNELS,
|
||||
.period_bytes_min = (64 * 4) * 10,
|
||||
|
@ -4357,10 +4761,12 @@ static int snd_hdsp_playback_open(struct snd_pcm_substream *substream)
|
|||
snd_hdsp_hw_rule_rate_out_channels, hdsp,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
||||
|
||||
if (RPM != hdsp->io_type) {
|
||||
hdsp->creg_spdif_stream = hdsp->creg_spdif;
|
||||
hdsp->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
||||
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4375,9 +4781,11 @@ static int snd_hdsp_playback_release(struct snd_pcm_substream *substream)
|
|||
|
||||
spin_unlock_irq(&hdsp->lock);
|
||||
|
||||
if (RPM != hdsp->io_type) {
|
||||
hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
||||
snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO, &hdsp->spdif_ctl->id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4616,7 +5024,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
|
|||
if (hdsp->io_type != H9632)
|
||||
info.adatsync_sync_check = (unsigned char)hdsp_adatsync_sync_check(hdsp);
|
||||
info.spdif_sync_check = (unsigned char)hdsp_spdif_sync_check(hdsp);
|
||||
for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != H9632) ? 3 : 1); ++i)
|
||||
for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i)
|
||||
info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
|
||||
info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
|
||||
info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
|
||||
|
@ -4636,6 +5044,9 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
|
|||
info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
|
||||
info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
|
||||
|
||||
} else if (hdsp->io_type == RPM) {
|
||||
info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp);
|
||||
info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp);
|
||||
}
|
||||
if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
|
||||
info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
|
||||
|
@ -4844,6 +5255,14 @@ static void snd_hdsp_initialize_channels(struct hdsp *hdsp)
|
|||
hdsp->ds_in_channels = hdsp->ds_out_channels = MULTIFACE_DS_CHANNELS;
|
||||
break;
|
||||
|
||||
case RPM:
|
||||
hdsp->card_name = "RME Hammerfall DSP + RPM";
|
||||
hdsp->ss_in_channels = RPM_CHANNELS-1;
|
||||
hdsp->ss_out_channels = RPM_CHANNELS;
|
||||
hdsp->ds_in_channels = RPM_CHANNELS-1;
|
||||
hdsp->ds_out_channels = RPM_CHANNELS;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never get here */
|
||||
break;
|
||||
|
@ -4930,6 +5349,9 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
|
|||
|
||||
/* caution: max length of firmware filename is 30! */
|
||||
switch (hdsp->io_type) {
|
||||
case RPM:
|
||||
fwfile = "rpm_firmware.bin";
|
||||
break;
|
||||
case Multiface:
|
||||
if (hdsp->firmware_rev == 0xa)
|
||||
fwfile = "multiface_firmware.bin";
|
||||
|
@ -5100,7 +5522,9 @@ static int __devinit snd_hdsp_create(struct snd_card *card,
|
|||
return 0;
|
||||
} else {
|
||||
snd_printk(KERN_INFO "Hammerfall-DSP: Firmware already present, initializing card.\n");
|
||||
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
|
||||
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
|
||||
hdsp->io_type = RPM;
|
||||
else if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version1)
|
||||
hdsp->io_type = Multiface;
|
||||
else
|
||||
hdsp->io_type = Digiface;
|
||||
|
|
|
@ -1389,15 +1389,9 @@ static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
|
|||
|
||||
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
|
||||
static const char *const texts[3] = {"AC'97", "IEC958", "ZV Port"};
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 3;
|
||||
if (info->value.enumerated.item > 2)
|
||||
info->value.enumerated.item = 2;
|
||||
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 3, texts);
|
||||
}
|
||||
|
||||
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
||||
|
|
|
@ -1626,7 +1626,6 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec)
|
|||
{
|
||||
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8350 *wm8350 = dev_get_platdata(codec->dev);
|
||||
int ret;
|
||||
|
||||
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
|
||||
WM8350_JDL_ENA | WM8350_JDR_ENA);
|
||||
|
@ -1641,15 +1640,9 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec)
|
|||
priv->hpr.jack = NULL;
|
||||
priv->mic.jack = NULL;
|
||||
|
||||
/* cancel any work waiting to be queued. */
|
||||
ret = cancel_delayed_work(&codec->delayed_work);
|
||||
|
||||
/* if there was any work waiting then we run it now and
|
||||
* wait for its completion */
|
||||
if (ret) {
|
||||
schedule_delayed_work(&codec->delayed_work, 0);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
flush_delayed_work_sync(&codec->delayed_work);
|
||||
|
||||
wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
|
|
|
@ -1476,25 +1476,6 @@ static int wm8753_resume(struct snd_soc_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function forces any delayed work to be queued and run.
|
||||
*/
|
||||
static int run_delayed_work(struct delayed_work *dwork)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* cancel any work waiting to be queued. */
|
||||
ret = cancel_delayed_work(dwork);
|
||||
|
||||
/* if there was any work waiting then we run it now and
|
||||
* wait for it's completion */
|
||||
if (ret) {
|
||||
schedule_delayed_work(dwork, 0);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8753_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
|
||||
|
@ -1544,7 +1525,7 @@ static int wm8753_probe(struct snd_soc_codec *codec)
|
|||
/* power down chip */
|
||||
static int wm8753_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
run_delayed_work(&codec->delayed_work);
|
||||
flush_delayed_work_sync(&codec->delayed_work);
|
||||
wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -67,25 +67,6 @@ static int pmdown_time = 5000;
|
|||
module_param(pmdown_time, int, 0);
|
||||
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
|
||||
|
||||
/*
|
||||
* This function forces any delayed work to be queued and run.
|
||||
*/
|
||||
static int run_delayed_work(struct delayed_work *dwork)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* cancel any work waiting to be queued. */
|
||||
ret = cancel_delayed_work(dwork);
|
||||
|
||||
/* if there was any work waiting then we run it now and
|
||||
* wait for it's completion */
|
||||
if (ret) {
|
||||
schedule_delayed_work(dwork, 0);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* codec register dump */
|
||||
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
|
||||
{
|
||||
|
@ -1016,7 +997,7 @@ static int soc_suspend(struct device *dev)
|
|||
|
||||
/* close any waiting streams and save state */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
run_delayed_work(&card->rtd[i].delayed_work);
|
||||
flush_delayed_work_sync(&card->rtd[i].delayed_work);
|
||||
card->rtd[i].codec->suspend_bias_level = card->rtd[i].codec->bias_level;
|
||||
}
|
||||
|
||||
|
@ -1689,7 +1670,7 @@ static int soc_remove(struct platform_device *pdev)
|
|||
/* make sure any delayed work runs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
run_delayed_work(&rtd->delayed_work);
|
||||
flush_delayed_work_sync(&rtd->delayed_work);
|
||||
}
|
||||
|
||||
/* remove and free each DAI */
|
||||
|
@ -1720,7 +1701,7 @@ static int soc_poweroff(struct device *dev)
|
|||
* now, we're shutting down so no imminent restart. */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
run_delayed_work(&rtd->delayed_work);
|
||||
flush_delayed_work_sync(&rtd->delayed_work);
|
||||
}
|
||||
|
||||
snd_soc_dapm_shutdown(card);
|
||||
|
|
|
@ -76,7 +76,10 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
|
|||
format = 1 << UAC_FORMAT_TYPE_I_PCM;
|
||||
}
|
||||
if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
|
||||
if (sample_width > sample_bytes * 8) {
|
||||
if (chip->usb_id == USB_ID(0x0582, 0x0016) /* Edirol SD-90 */ &&
|
||||
sample_width == 24 && sample_bytes == 2)
|
||||
sample_bytes = 3;
|
||||
else if (sample_width > sample_bytes * 8) {
|
||||
snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
|
||||
chip->dev->devnum, fp->iface, fp->altsetting,
|
||||
sample_width, sample_bytes);
|
||||
|
|
|
@ -850,8 +850,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
|
|||
return;
|
||||
}
|
||||
|
||||
memset(urb->transfer_buffer + count, 0xFD, 9 - count);
|
||||
urb->transfer_buffer_length = count;
|
||||
memset(urb->transfer_buffer + count, 0xFD, ep->max_transfer - count);
|
||||
urb->transfer_buffer_length = ep->max_transfer;
|
||||
}
|
||||
|
||||
static struct usb_protocol_ops snd_usbmidi_122l_ops = {
|
||||
|
@ -1295,6 +1295,13 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
|
|||
case USB_ID(0x1a86, 0x752d): /* QinHeng CH345 "USB2.0-MIDI" */
|
||||
ep->max_transfer = 4;
|
||||
break;
|
||||
/*
|
||||
* Some devices only work with 9 bytes packet size:
|
||||
*/
|
||||
case USB_ID(0x0644, 0x800E): /* Tascam US-122L */
|
||||
case USB_ID(0x0644, 0x800F): /* Tascam US-144 */
|
||||
ep->max_transfer = 9;
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < OUTPUT_URBS; ++i) {
|
||||
buffer = usb_alloc_coherent(umidi->dev,
|
||||
|
@ -1729,13 +1736,7 @@ static int roland_load_info(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
static const char *const names[] = { "High Load", "Light Load" };
|
||||
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
info->count = 1;
|
||||
info->value.enumerated.items = 2;
|
||||
if (info->value.enumerated.item > 1)
|
||||
info->value.enumerated.item = 1;
|
||||
strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
|
||||
return 0;
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int roland_load_get(struct snd_kcontrol *kcontrol,
|
||||
|
|
|
@ -1633,18 +1633,11 @@ static int parse_audio_extension_unit(struct mixer_build *state, int unitid, voi
|
|||
static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct usb_mixer_elem_info *cval = kcontrol->private_data;
|
||||
char **itemlist = (char **)kcontrol->private_value;
|
||||
const char **itemlist = (const char **)kcontrol->private_value;
|
||||
|
||||
if (snd_BUG_ON(!itemlist))
|
||||
return -EINVAL;
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = cval->max;
|
||||
if (uinfo->value.enumerated.item >= cval->max)
|
||||
uinfo->value.enumerated.item = cval->max - 1;
|
||||
strlcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item],
|
||||
sizeof(uinfo->value.enumerated.name));
|
||||
return 0;
|
||||
return snd_ctl_enum_info(uinfo, 1, cval->max, itemlist);
|
||||
}
|
||||
|
||||
/* get callback for selector unit */
|
||||
|
|
|
@ -705,11 +705,11 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
|||
.data = (const struct snd_usb_audio_quirk[]) {
|
||||
{
|
||||
.ifnum = 0,
|
||||
.type = QUIRK_IGNORE_INTERFACE
|
||||
.type = QUIRK_AUDIO_STANDARD_INTERFACE
|
||||
},
|
||||
{
|
||||
.ifnum = 1,
|
||||
.type = QUIRK_IGNORE_INTERFACE
|
||||
.type = QUIRK_AUDIO_STANDARD_INTERFACE
|
||||
},
|
||||
{
|
||||
.ifnum = 2,
|
||||
|
|
|
@ -273,14 +273,15 @@ static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
|
|||
struct file *file, poll_table *wait)
|
||||
{
|
||||
struct us122l *us122l = hw->private_data;
|
||||
struct usb_stream *s = us122l->sk.s;
|
||||
unsigned *polled;
|
||||
unsigned int mask;
|
||||
|
||||
poll_wait(file, &us122l->sk.sleep, wait);
|
||||
|
||||
switch (s->state) {
|
||||
case usb_stream_ready:
|
||||
mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
|
||||
if (mutex_trylock(&us122l->mutex)) {
|
||||
struct usb_stream *s = us122l->sk.s;
|
||||
if (s && s->state == usb_stream_ready) {
|
||||
if (us122l->first == file)
|
||||
polled = &s->periods_polled;
|
||||
else
|
||||
|
@ -288,14 +289,10 @@ static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw,
|
|||
if (*polled != s->periods_done) {
|
||||
*polled = s->periods_done;
|
||||
mask = POLLIN | POLLOUT | POLLWRNORM;
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
} else
|
||||
mask = 0;
|
||||
break;
|
||||
default:
|
||||
mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&us122l->mutex);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
@ -381,6 +378,7 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
|||
{
|
||||
struct usb_stream_config *cfg;
|
||||
struct us122l *us122l = hw->private_data;
|
||||
struct usb_stream *s;
|
||||
unsigned min_period_frames;
|
||||
int err = 0;
|
||||
bool high_speed;
|
||||
|
@ -426,18 +424,18 @@ static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
|||
snd_power_wait(hw->card, SNDRV_CTL_POWER_D0);
|
||||
|
||||
mutex_lock(&us122l->mutex);
|
||||
s = us122l->sk.s;
|
||||
if (!us122l->master)
|
||||
us122l->master = file;
|
||||
else if (us122l->master != file) {
|
||||
if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) {
|
||||
if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg))) {
|
||||
err = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
us122l->slave = file;
|
||||
}
|
||||
if (!us122l->sk.s ||
|
||||
memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) ||
|
||||
us122l->sk.s->state == usb_stream_xrun) {
|
||||
if (!s || memcmp(cfg, &s->cfg, sizeof(*cfg)) ||
|
||||
s->state == usb_stream_xrun) {
|
||||
us122l_stop(us122l);
|
||||
if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames))
|
||||
err = -EIO;
|
||||
|
@ -448,6 +446,7 @@ unlock:
|
|||
mutex_unlock(&us122l->mutex);
|
||||
free:
|
||||
kfree(cfg);
|
||||
wake_up_all(&us122l->sk.sleep);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче