aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2018-06-04 11:41:48 +0200
committerTakashi Iwai <tiwai@suse.de>2018-06-04 11:42:27 +0200
commitcdbc653a04ee692a7105a96e8dd6055d9971d45c (patch)
treec1282680b3115edfd693a27bc6758208534ac557 /sound/pci
parentALSA: hda - Fix runtime PM (diff)
parentALSA: pci/hda: Remove unused, broken, header file (diff)
downloadlinux-dev-cdbc653a04ee692a7105a96e8dd6055d9971d45c.tar.xz
linux-dev-cdbc653a04ee692a7105a96e8dd6055d9971d45c.zip
Merge branch 'for-next' into for-linus
4.18-rc1 merge material. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/ac97/ac97_proc.c4
-rw-r--r--sound/pci/ad1889.c4
-rw-r--r--sound/pci/asihpi/asihpi.c12
-rw-r--r--sound/pci/asihpi/hpioctl.c4
-rw-r--r--sound/pci/ca0106/ca0106_proc.c6
-rw-r--r--sound/pci/cmipci.c2
-rw-r--r--sound/pci/cs46xx/cs46xx.c2
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c2
-rw-r--r--sound/pci/cs46xx/dsp_spos.c14
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c2
-rw-r--r--sound/pci/ctxfi/cttimer.c2
-rw-r--r--sound/pci/ctxfi/xfi.c4
-rw-r--r--sound/pci/echoaudio/echoaudio.c2
-rw-r--r--sound/pci/echoaudio/echoaudio.h6
-rw-r--r--sound/pci/emu10k1/emu10k1x.c2
-rw-r--r--sound/pci/emu10k1/emufx.c11
-rw-r--r--sound/pci/emu10k1/emupcm.c2
-rw-r--r--sound/pci/emu10k1/emuproc.c24
-rw-r--r--sound/pci/emu10k1/memory.c6
-rw-r--r--sound/pci/hda/Kconfig4
-rw-r--r--sound/pci/hda/hda_auto_parser.c10
-rw-r--r--sound/pci/hda/hda_codec.c70
-rw-r--r--sound/pci/hda/hda_controller.c4
-rw-r--r--sound/pci/hda/hda_generic.c29
-rw-r--r--sound/pci/hda/hda_intel.c11
-rw-r--r--sound/pci/hda/hda_sysfs.c20
-rw-r--r--sound/pci/hda/hp_x360_helper.c95
-rw-r--r--sound/pci/hda/local.h40
-rw-r--r--sound/pci/hda/patch_ca0132.c2997
-rw-r--r--sound/pci/hda/patch_conexant.c7
-rw-r--r--sound/pci/hda/patch_hdmi.c2
-rw-r--r--sound/pci/hda/patch_realtek.c55
-rw-r--r--sound/pci/ice1712/pontis.c2
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c2
-rw-r--r--sound/pci/lola/lola_proc.c2
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c14
-rw-r--r--sound/pci/pcxhr/pcxhr.c2
37 files changed, 3200 insertions, 277 deletions
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 6320bf084e47..e120a11c69e8 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -448,7 +448,7 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read);
#ifdef CONFIG_SND_DEBUG
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = snd_ac97_proc_regs_write;
#endif
if (snd_info_register(entry) < 0) {
@@ -474,7 +474,7 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
sprintf(name, "codec97#%d", bus->num);
if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
entry = NULL;
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 0bf2c04eeada..d9c54c08e2db 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -258,7 +258,7 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
while (!(ad1889_readw(chip, AD_AC97_ACIC) & AD_AC97_ACIC_ACRDY)
&& --retry)
- mdelay(1);
+ usleep_range(1000, 2000);
if (!retry) {
dev_err(chip->card->dev, "[%s] Link is not ready.\n",
__func__);
@@ -872,7 +872,7 @@ snd_ad1889_init(struct snd_ad1889 *chip)
ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
- mdelay(10);
+ usleep_range(10000, 11000);
/* enable Master and Target abort interrupts */
ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index 720361455c60..64e0961f93ba 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -69,27 +69,27 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static bool enable_hpi_hwdep = 1;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
-module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
+module_param(enable_hpi_hwdep, bool, 0644);
MODULE_PARM_DESC(enable_hpi_hwdep,
"ALSA enable HPI hwdep for AudioScience soundcard ");
/* identify driver */
#ifdef KERNEL_ALSA_BUILD
static char *build_info = "Built using headers from kernel source";
-module_param(build_info, charp, S_IRUGO);
+module_param(build_info, charp, 0444);
MODULE_PARM_DESC(build_info, "Built using headers from kernel source");
#else
static char *build_info = "Built within ALSA source";
-module_param(build_info, charp, S_IRUGO);
+module_param(build_info, charp, 0444);
MODULE_PARM_DESC(build_info, "Built within ALSA source");
#endif
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index b1a2a7ea4172..7d049569012c 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -46,14 +46,14 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
-module_param(prealloc_stream_buf, int, S_IRUGO);
+module_param(prealloc_stream_buf, int, 0444);
MODULE_PARM_DESC(prealloc_stream_buf,
"Preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
-module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
+module_param(hpi_debug_level, int, 0644);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index 9b2b8b38122f..a2c85cc37972 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -431,7 +431,7 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu)
if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
entry->c.text.write = snd_ca0106_proc_reg_write32;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
@@ -440,12 +440,12 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu)
if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
entry->c.text.write = snd_ca0106_proc_reg_write;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
entry->c.text.write = snd_ca0106_proc_i2c_write;
entry->private_data = emu;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 26a657870664..452cc79b44af 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -1139,7 +1139,7 @@ static int save_mixer_state(struct cmipci *cm)
struct snd_ctl_elem_value *val;
unsigned int i;
- val = kmalloc(sizeof(*val), GFP_ATOMIC);
+ val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
return -ENOMEM;
for (i = 0; i < CM_SAVED_MIXERS; i++) {
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 655fbea1692c..4910d3f46d4b 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -58,7 +58,7 @@ MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
module_param_array(external_amp, bool, NULL, 0444);
-MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
+MODULE_PARM_DESC(external_amp, "Force to enable external amplifier.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
module_param_array(mmap_valid, bool, NULL, 0444);
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 0020fd0efc46..ed1251c5f449 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2849,7 +2849,7 @@ static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
entry->private_data = chip;
entry->c.ops = &snd_cs46xx_proc_io_ops;
entry->size = region->size;
- entry->mode = S_IFREG | S_IRUSR;
+ entry->mode = S_IFREG | 0400;
}
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index aa61615288ff..c44eadef64ae 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -798,7 +798,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ entry->mode = S_IFDIR | 0555;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -814,7 +814,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -826,7 +826,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_modules_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -838,7 +838,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -850,7 +850,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -862,7 +862,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -874,7 +874,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_scb_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 7488e1b7a770..abb01ce66983 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -271,7 +271,7 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = scb_info;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index 08e874e9a7f6..2099e9ce441a 100644
--- a/sound/pci/ctxfi/cttimer.c
+++ b/sound/pci/ctxfi/cttimer.c
@@ -17,7 +17,7 @@
static bool use_system_timer;
MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
-module_param(use_system_timer, bool, S_IRUGO);
+module_param(use_system_timer, bool, 0444);
struct ct_timer_ops {
void (*init)(struct ct_timer_instance *);
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index f2f32779de98..b2874220be1b 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -26,9 +26,9 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
static unsigned int reference_rate = 48000;
static unsigned int multiple = 2;
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
-module_param(reference_rate, uint, S_IRUGO);
+module_param(reference_rate, uint, 0444);
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
-module_param(multiple, uint, S_IRUGO);
+module_param(multiple, uint, 0444);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 0935a5c8741f..358ef7dcf410 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -59,7 +59,7 @@ static int get_firmware(const struct firmware **fw_entry,
dev_dbg(chip->card->dev,
"firmware requested: %s\n", card_fw[fw_index].data);
snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
- err = request_firmware(fw_entry, name, pci_device(chip));
+ err = request_firmware(fw_entry, name, &chip->pci->dev);
if (err < 0)
dev_err(chip->card->dev,
"get_firmware(): Firmware not available (%d)\n", err);
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 152ce158583c..44b390a667d5 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -559,10 +559,4 @@ static inline int monitor_index(const struct echoaudio *chip, int out, int in)
return out * num_busses_in(chip) + in;
}
-
-#ifndef pci_device
-#define pci_device(chip) (&chip->pci->dev)
-#endif
-
-
#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 2c2b12a06177..611589cbdad6 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1070,7 +1070,7 @@ static int snd_emu10k1x_proc_init(struct emu10k1x *emu)
if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
entry->c.text.write = snd_emu10k1x_proc_reg_write;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->private_data = emu;
}
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index a2b56b188be4..b45a01bb73e5 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -170,7 +170,7 @@ static char *audigy_outs[32] = {
/* 0x0f */ "Rear Right",
/* 0x10 */ "AC97 Front Left",
/* 0x11 */ "AC97 Front Right",
- /* 0x12 */ "ADC Caputre Left",
+ /* 0x12 */ "ADC Capture Left",
/* 0x13 */ "ADC Capture Right",
/* 0x14 */ NULL,
/* 0x15 */ NULL,
@@ -421,14 +421,10 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
snd_fx8010_irq_handler_t *handler,
unsigned char gpr_running,
void *private_data,
- struct snd_emu10k1_fx8010_irq **r_irq)
+ struct snd_emu10k1_fx8010_irq *irq)
{
- struct snd_emu10k1_fx8010_irq *irq;
unsigned long flags;
- irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
- if (irq == NULL)
- return -ENOMEM;
irq->handler = handler;
irq->gpr_running = gpr_running;
irq->private_data = private_data;
@@ -443,8 +439,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
emu->fx8010.irq_handlers = irq;
}
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- if (r_irq)
- *r_irq = irq;
return 0;
}
@@ -468,7 +462,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
tmp->next = tmp->next->next;
}
spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- kfree(irq);
return 0;
}
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index cefe613ef7b7..d39458ab251f 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1724,7 +1724,7 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+ snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq);
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
pcm->tram_shift = 0;
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index bde0d1954f56..b57008031792 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -135,7 +135,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
/* 15 */ "Rear Right",
/* 16 */ "AC97 Front Left",
/* 17 */ "AC97 Front Right",
- /* 18 */ "ADC Caputre Left",
+ /* 18 */ "ADC Capture Left",
/* 19 */ "ADC Capture Right",
/* 20 */ "???",
/* 21 */ "???",
@@ -574,32 +574,32 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
entry->c.text.write = snd_emu_proc_io_reg_write;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
#endif
@@ -621,35 +621,35 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->c.text.read = snd_emu10k1_proc_acode_read;
}
return 0;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 5865f3b90b34..dbc7d8d0e1c4 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -248,13 +248,13 @@ __found_pages:
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
{
if (addr & ~emu->dma_mask) {
- dev_err(emu->card->dev,
+ dev_err_ratelimited(emu->card->dev,
"max memory size is 0x%lx (addr = 0x%lx)!!\n",
emu->dma_mask, (unsigned long)addr);
return 0;
}
if (addr & (EMUPAGESIZE-1)) {
- dev_err(emu->card->dev, "page is not aligned\n");
+ dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
return 0;
}
return 1;
@@ -345,7 +345,7 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
else
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
- dev_err(emu->card->dev,
+ dev_err_ratelimited(emu->card->dev,
"emu: failure page = %d\n", idx);
mutex_unlock(&hdr->block_mutex);
return NULL;
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index f7a492c382d9..4235907b7858 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -127,11 +127,15 @@ comment "Set to Y if you want auto-loading the codec driver"
config SND_HDA_CODEC_HDMI
tristate "Build HDMI/DisplayPort HD-audio codec support"
+ select SND_DYNAMIC_MINORS
help
Say Y or M here to include HDMI and DisplayPort HD-audio codec
support in snd-hda-intel driver. This includes all AMD/ATI,
Intel and Nvidia HDMI/DisplayPort codecs.
+ Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS
+ to assure the multiple streams for DP-MST support.
+
comment "Set to Y if you want auto-loading the codec driver"
depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index d3ea73171a3d..b9a6b66aeb0e 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -793,11 +793,11 @@ EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
*/
void snd_hda_apply_verbs(struct hda_codec *codec)
{
+ const struct hda_verb **v;
int i;
- for (i = 0; i < codec->verbs.used; i++) {
- struct hda_verb **v = snd_array_elem(&codec->verbs, i);
+
+ snd_array_for_each(&codec->verbs, i, v)
snd_hda_sequence_write(codec, *v);
- }
}
EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
@@ -890,10 +890,10 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
+ const struct hda_pincfg *pin;
int i;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
hda_nid_t nid = pin->nid;
u32 cfg = pin->cfg;
const struct hda_pintbl *t_pins;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 5bc3a7468e17..08151f3c0b13 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -481,9 +481,10 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
struct snd_array *array,
hda_nid_t nid)
{
+ struct hda_pincfg *pin;
int i;
- for (i = 0; i < array->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(array, i);
+
+ snd_array_for_each(array, i, pin) {
if (pin->nid == nid)
return pin;
}
@@ -618,14 +619,15 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
*/
void snd_hda_shutup_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
+
/* don't shut up pins when unloading the driver; otherwise it breaks
* the default pin setup at the next load of the driver
*/
if (codec->bus->shutdown)
return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
/* use read here for syncing after issuing each verb */
snd_hda_codec_read(codec, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
@@ -638,13 +640,14 @@ EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
static void restore_shutup_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
+
if (!codec->pins_shutup)
return;
if (codec->bus->shutdown)
return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
snd_hda_codec_write(codec, pin->nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
pin->ctrl);
@@ -697,8 +700,7 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
struct hda_cvt_setup *p;
int i;
- for (i = 0; i < codec->cvt_setups.used; i++) {
- p = snd_array_elem(&codec->cvt_setups, i);
+ snd_array_for_each(&codec->cvt_setups, i, p) {
if (p->nid == nid)
return p;
}
@@ -1076,8 +1078,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
/* make other inactive cvts with the same stream-tag dirty */
type = get_wcaps_type(get_wcaps(codec, nid));
list_for_each_codec(c, codec->bus) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- p = snd_array_elem(&c->cvt_setups, i);
+ snd_array_for_each(&c->cvt_setups, i, p) {
if (!p->active && p->stream_tag == stream_tag &&
get_wcaps_type(get_wcaps(c, p->nid)) == type)
p->dirty = 1;
@@ -1140,12 +1141,11 @@ static void really_cleanup_stream(struct hda_codec *codec,
static void purify_inactive_streams(struct hda_codec *codec)
{
struct hda_codec *c;
+ struct hda_cvt_setup *p;
int i;
list_for_each_codec(c, codec->bus) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- struct hda_cvt_setup *p;
- p = snd_array_elem(&c->cvt_setups, i);
+ snd_array_for_each(&c->cvt_setups, i, p) {
if (p->dirty)
really_cleanup_stream(c, p);
}
@@ -1156,10 +1156,10 @@ static void purify_inactive_streams(struct hda_codec *codec)
/* clean up all streams; called from suspend */
static void hda_cleanup_all_streams(struct hda_codec *codec)
{
+ struct hda_cvt_setup *p;
int i;
- for (i = 0; i < codec->cvt_setups.used; i++) {
- struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
+ snd_array_for_each(&codec->cvt_setups, i, p) {
if (p->stream_tag)
really_cleanup_stream(codec, p);
}
@@ -1493,10 +1493,10 @@ static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
val1 = ((int)val1) * ((int)val2);
if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
val2 |= TLV_DB_SCALE_MUTE;
- tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
- tlv[1] = 2 * sizeof(unsigned int);
- tlv[2] = val1;
- tlv[3] = val2;
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
}
/**
@@ -1544,10 +1544,10 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
step = (step + 1) * 25;
- tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
- tlv[1] = 2 * sizeof(unsigned int);
- tlv[2] = -nums * step;
- tlv[3] = step;
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
}
EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
@@ -1845,10 +1845,10 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
} else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
tlv = kctl->tlv.p;
- if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE)
+ if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
return 0;
- step = tlv[3];
+ step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
step &= ~TLV_DB_SCALE_MUTE;
if (!step)
return 0;
@@ -1860,7 +1860,7 @@ static int init_slave_0dB(struct snd_kcontrol *slave,
}
arg->step = step;
- val = -tlv[2] / step;
+ val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
if (val > 0) {
put_kctl_with_value(slave, val);
return val;
@@ -2175,6 +2175,8 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
int idx = kcontrol->private_value;
struct hda_spdif_out *spdif;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
ucontrol->value.iec958.status[0] = spdif->status & 0xff;
@@ -2282,6 +2284,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned short val;
int change;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
nid = spdif->nid;
@@ -2308,6 +2312,8 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
int idx = kcontrol->private_value;
struct hda_spdif_out *spdif;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
@@ -2336,6 +2342,8 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
unsigned short val;
int change;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
nid = spdif->nid;
@@ -2461,10 +2469,10 @@ EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
hda_nid_t nid)
{
+ struct hda_spdif_out *spdif;
int i;
- for (i = 0; i < codec->spdif_out.used; i++) {
- struct hda_spdif_out *spdif =
- snd_array_elem(&codec->spdif_out, i);
+
+ snd_array_for_each(&codec->spdif_out, i, spdif) {
if (spdif->nid == nid)
return spdif;
}
@@ -2483,6 +2491,8 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
{
struct hda_spdif_out *spdif;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
spdif->nid = (u16)-1;
@@ -2503,6 +2513,8 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
struct hda_spdif_out *spdif;
unsigned short val;
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
mutex_lock(&codec->spdif_mutex);
spdif = snd_array_elem(&codec->spdif_out, idx);
if (spdif->nid != nid) {
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index d1eb14842340..a12e594d4e3b 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -748,8 +748,10 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
return err;
strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (apcm == NULL)
+ if (apcm == NULL) {
+ snd_device_free(chip->card, pcm);
return -ENOMEM;
+ }
apcm->chip = chip;
apcm->pcm = pcm;
apcm->codec = codec;
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5cc65093d941..db773e219aaa 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -264,10 +264,10 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
int anchor_nid)
{
struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
int i;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if (path->depth <= 0)
continue;
if ((!from_nid || path->path[0] == from_nid) &&
@@ -325,10 +325,10 @@ EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
int i;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if (path->path[0] == nid)
return true;
}
@@ -351,11 +351,11 @@ static bool is_reachable_path(struct hda_codec *codec,
static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
{
struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
int i;
val &= AMP_VAL_COMPARE_MASK;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
+ snd_array_for_each(&spec->paths, i, path) {
if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
return true;
}
@@ -638,13 +638,13 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
{
struct hda_gen_spec *spec = codec->spec;
int type = get_wcaps_type(get_wcaps(codec, nid));
+ const struct nid_path *path;
int i, n;
if (nid == codec->core.afg)
return true;
- for (n = 0; n < spec->paths.used; n++) {
- struct nid_path *path = snd_array_elem(&spec->paths, n);
+ snd_array_for_each(&spec->paths, n, path) {
if (!path->active)
continue;
if (codec->power_save_node) {
@@ -2065,7 +2065,7 @@ static int parse_output_paths(struct hda_codec *codec)
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, spec->vmaster_tlv);
if (spec->dac_min_mute)
- spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+ spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
}
}
@@ -2696,10 +2696,10 @@ static const struct snd_kcontrol_new out_jack_mode_enum = {
static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
{
struct hda_gen_spec *spec = codec->spec;
+ const struct snd_kcontrol_new *kctl;
int i;
- for (i = 0; i < spec->kctls.used; i++) {
- struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
+ snd_array_for_each(&spec->kctls, i, kctl) {
if (!strcmp(kctl->name, name) && kctl->index == idx)
return true;
}
@@ -4021,8 +4021,7 @@ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
struct nid_path *path;
int n;
- for (n = 0; n < spec->paths.used; n++) {
- path = snd_array_elem(&spec->paths, n);
+ snd_array_for_each(&spec->paths, n, path) {
if (!path->depth)
continue;
if (path->path[0] == nid ||
@@ -5831,10 +5830,10 @@ static void init_digital(struct hda_codec *codec)
*/
static void clear_unsol_on_unused_pins(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
hda_nid_t nid = pin->nid;
if (is_jack_detectable(codec, nid) &&
!snd_hda_jack_tbl_get(codec, nid))
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index a0c93b9c9a28..1ae1850b3bfd 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2209,7 +2209,18 @@ static struct snd_pci_quirk power_save_blacklist[] = {
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0),
/* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */
+ SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */
+ SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */
+ SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0),
/* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
/* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
index 9b7efece4484..6ec79c58d48d 100644
--- a/sound/pci/hda/hda_sysfs.c
+++ b/sound/pci/hda/hda_sysfs.c
@@ -80,10 +80,10 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
struct snd_array *list,
char *buf)
{
+ const struct hda_pincfg *pin;
int i, len = 0;
mutex_lock(&codec->user_mutex);
- for (i = 0; i < list->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(list, i);
+ snd_array_for_each(list, i, pin) {
len += sprintf(buf + len, "0x%02x 0x%08x\n",
pin->nid, pin->cfg);
}
@@ -217,10 +217,10 @@ static ssize_t init_verbs_show(struct device *dev,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_verb *v;
int i, len = 0;
mutex_lock(&codec->user_mutex);
- for (i = 0; i < codec->init_verbs.used; i++) {
- struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
+ snd_array_for_each(&codec->init_verbs, i, v) {
len += snprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param);
@@ -267,10 +267,10 @@ static ssize_t hints_show(struct device *dev,
char *buf)
{
struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_hint *hint;
int i, len = 0;
mutex_lock(&codec->user_mutex);
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ snd_array_for_each(&codec->hints, i, hint) {
len += snprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val);
}
@@ -280,10 +280,10 @@ static ssize_t hints_show(struct device *dev,
static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
{
+ struct hda_hint *hint;
int i;
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ snd_array_for_each(&codec->hints, i, hint) {
if (!strcmp(hint->key, key))
return hint;
}
@@ -783,13 +783,13 @@ void snd_hda_sysfs_init(struct hda_codec *codec)
void snd_hda_sysfs_clear(struct hda_codec *codec)
{
#ifdef CONFIG_SND_HDA_RECONFIG
+ struct hda_hint *hint;
int i;
/* clear init verbs */
snd_array_free(&codec->init_verbs);
/* clear hints */
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
+ snd_array_for_each(&codec->hints, i, hint) {
kfree(hint->key); /* we don't need to free hint->val */
}
snd_array_free(&codec->hints);
diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/pci/hda/hp_x360_helper.c
new file mode 100644
index 000000000000..969542c57358
--- /dev/null
+++ b/sound/pci/hda/hp_x360_helper.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Fixes for HP X360 laptops with top B&O speakers
+ * to be included from codec driver
+ */
+
+static void alc295_fixup_hp_top_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x90170110 },
+ { }
+ };
+ static const struct coef_fw alc295_hp_speakers_coefs[] = {
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024),
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc295_fixup_disable_dac3(codec, fix, action);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, alc295_hp_speakers_coefs);
+ break;
+ }
+}
diff --git a/sound/pci/hda/local.h b/sound/pci/hda/local.h
deleted file mode 100644
index 3b8b7d78f9e0..000000000000
--- a/sound/pci/hda/local.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- */
-
-#ifndef __HDAC_LOCAL_H
-#define __HDAC_LOCAL_H
-
-int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm);
-
-#define get_wcaps(codec, nid) \
- hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP)
-/* get the widget type from widget capability bits */
-static inline int get_wcaps_type(unsigned int wcaps)
-{
- if (!wcaps)
- return -1; /* invalid type */
- return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-}
-
-#define get_pin_caps(codec, nid) \
- hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)
-
-static inline
-unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid)
-{
- unsigned int val;
-
- if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
- return -1;
- return val;
-}
-
-#define get_amp_caps(codec, nid, dir) \
- hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP)
-
-#define get_power_caps(codec, nid) \
- hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)
-
-#endif /* __HDAC_LOCAL_H */
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 768ea8651993..292e2c592c17 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -28,6 +28,9 @@
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/pci.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -39,9 +42,15 @@
/* Enable this to see controls for tuning purpose. */
/*#define ENABLE_TUNING_CONTROLS*/
+#ifdef ENABLE_TUNING_CONTROLS
+#include <sound/tlv.h>
+#endif
+
#define FLOAT_ZERO 0x00000000
#define FLOAT_ONE 0x3f800000
#define FLOAT_TWO 0x40000000
+#define FLOAT_THREE 0x40400000
+#define FLOAT_EIGHT 0x41000000
#define FLOAT_MINUS_5 0xc0a00000
#define UNSOL_TAG_DSP 0x16
@@ -72,16 +81,22 @@
#define SCP_GET 1
#define EFX_FILE "ctefx.bin"
+#define SBZ_EFX_FILE "ctefx-sbz.bin"
+#define R3DI_EFX_FILE "ctefx-r3di.bin"
#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
MODULE_FIRMWARE(EFX_FILE);
+MODULE_FIRMWARE(SBZ_EFX_FILE);
+MODULE_FIRMWARE(R3DI_EFX_FILE);
#endif
-static char *dirstr[2] = { "Playback", "Capture" };
+static const char *const dirstr[2] = { "Playback", "Capture" };
+#define NUM_OF_OUTPUTS 3
enum {
SPEAKER_OUT,
- HEADPHONE_OUT
+ HEADPHONE_OUT,
+ SURROUND_OUT
};
enum {
@@ -89,6 +104,15 @@ enum {
LINE_MIC_IN
};
+/* Strings for Input Source Enum Control */
+static const char *const in_src_str[3] = {"Rear Mic", "Line", "Front Mic" };
+#define IN_SRC_NUM_OF_INPUTS 3
+enum {
+ REAR_MIC,
+ REAR_LINE_IN,
+ FRONT_MIC,
+};
+
enum {
#define VNODE_START_NID 0x80
VNID_SPK = VNODE_START_NID, /* Speaker vnid */
@@ -122,13 +146,28 @@ enum {
VOICEFX = IN_EFFECT_END_NID,
PLAY_ENHANCEMENT,
CRYSTAL_VOICE,
- EFFECT_END_NID
+ EFFECT_END_NID,
+ OUTPUT_SOURCE_ENUM,
+ INPUT_SOURCE_ENUM,
+ XBASS_XOVER,
+ EQ_PRESET_ENUM,
+ SMART_VOLUME_ENUM,
+ MIC_BOOST_ENUM
#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
};
/* Effects values size*/
#define EFFECT_VALS_MAX_COUNT 12
+/*
+ * Default values for the effect slider controls, they are in order of their
+ * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then
+ * X-bass.
+ */
+static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50};
+/* Amount of effect level sliders for ca0132_alt controls. */
+#define EFFECT_LEVEL_SLIDERS 5
+
/* Latency introduced by DSP blocks in milliseconds. */
#define DSP_CAPTURE_INIT_LATENCY 0
#define DSP_CRYSTAL_VOICE_LATENCY 124
@@ -150,7 +189,7 @@ struct ct_effect {
#define EFX_DIR_OUT 0
#define EFX_DIR_IN 1
-static struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
+static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
{ .name = "Surround",
.nid = SURROUND,
.mid = 0x96,
@@ -277,7 +316,7 @@ struct ct_tuning_ctl {
unsigned int def_val;/*effect default values*/
};
-static struct ct_tuning_ctl ca0132_tuning_ctls[] = {
+static const struct ct_tuning_ctl ca0132_tuning_ctls[] = {
{ .name = "Wedge Angle",
.parent_nid = VOICE_FOCUS,
.nid = WEDGE_ANGLE,
@@ -392,14 +431,14 @@ struct ct_voicefx_preset {
unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
};
-static struct ct_voicefx ca0132_voicefx = {
+static const struct ct_voicefx ca0132_voicefx = {
.name = "VoiceFX Capture Switch",
.nid = VOICEFX,
.mid = 0x95,
.reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
};
-static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
+static const struct ct_voicefx_preset ca0132_voicefx_presets[] = {
{ .name = "Neutral",
.vals = { 0x00000000, 0x43C80000, 0x44AF0000,
0x44FA0000, 0x3F800000, 0x3F800000,
@@ -472,6 +511,161 @@ static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
}
};
+/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */
+
+#define EQ_PRESET_MAX_PARAM_COUNT 11
+
+struct ct_eq {
+ char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_eq_preset {
+ char *name; /*preset name*/
+ unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT];
+};
+
+static const struct ct_eq ca0132_alt_eq_enum = {
+ .name = "FX: Equalizer Preset Switch",
+ .nid = EQ_PRESET_ENUM,
+ .mid = 0x96,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
+};
+
+
+static const struct ct_eq_preset ca0132_alt_eq_presets[] = {
+ { .name = "Flat",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 }
+ },
+ { .name = "Acoustic",
+ .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Classical",
+ .vals = { 0x00000000, 0x00000000, 0x40C00000,
+ 0x40C00000, 0x40466666, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "Country",
+ .vals = { 0x00000000, 0xBF99999A, 0x00000000,
+ 0x3FA66666, 0x3FA66666, 0x3F8CCCCD,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40466666, 0x40800000 }
+ },
+ { .name = "Dance",
+ .vals = { 0x00000000, 0xBF99999A, 0x40000000,
+ 0x40466666, 0x40866666, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Jazz",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x3F8CCCCD, 0x40800000, 0x40800000,
+ 0x40800000, 0x00000000, 0x3F8CCCCD,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "New Age",
+ .vals = { 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x3F8CCCCD, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Pop",
+ .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000,
+ 0x40000000, 0x40000000, 0x00000000,
+ 0xBF99999A, 0xBF99999A, 0x00000000,
+ 0x40466666, 0x40C00000 }
+ },
+ { .name = "Rock",
+ .vals = { 0x00000000, 0xBF99999A, 0xBF99999A,
+ 0x3F8CCCCD, 0x40000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Vocal",
+ .vals = { 0x00000000, 0xC0000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x40466666,
+ 0x40800000, 0x40466666, 0x00000000,
+ 0x00000000, 0x3F8CCCCD }
+ }
+};
+
+/* DSP command sequences for ca0132_alt_select_out */
+#define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */
+struct ca0132_alt_out_set {
+ char *name; /*preset name*/
+ unsigned char commands;
+ unsigned int mids[ALT_OUT_SET_MAX_COMMANDS];
+ unsigned int reqs[ALT_OUT_SET_MAX_COMMANDS];
+ unsigned int vals[ALT_OUT_SET_MAX_COMMANDS];
+};
+
+static const struct ca0132_alt_out_set alt_out_presets[] = {
+ { .name = "Line Out",
+ .commands = 7,
+ .mids = { 0x96, 0x96, 0x96, 0x8F,
+ 0x96, 0x96, 0x96 },
+ .reqs = { 0x19, 0x17, 0x18, 0x01,
+ 0x1F, 0x15, 0x3A },
+ .vals = { 0x3F000000, 0x42A00000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000 }
+ },
+ { .name = "Headphone",
+ .commands = 7,
+ .mids = { 0x96, 0x96, 0x96, 0x8F,
+ 0x96, 0x96, 0x96 },
+ .reqs = { 0x19, 0x17, 0x18, 0x01,
+ 0x1F, 0x15, 0x3A },
+ .vals = { 0x3F000000, 0x42A00000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000 }
+ },
+ { .name = "Surround",
+ .commands = 8,
+ .mids = { 0x96, 0x8F, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96 },
+ .reqs = { 0x18, 0x01, 0x1F, 0x15,
+ 0x3A, 0x1A, 0x1B, 0x1C },
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 }
+ }
+};
+
+/*
+ * DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
+ * and I don't know what the third req is, but it's always zero. I assume it's
+ * some sort of update or set command to tell the DSP there's new volume info.
+ */
+#define DSP_VOL_OUT 0
+#define DSP_VOL_IN 1
+
+struct ct_dsp_volume_ctl {
+ hda_nid_t vnid;
+ int mid; /* module ID*/
+ unsigned int reqs[3]; /* scp req ID */
+};
+
+static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
+ { .vnid = VNID_SPK,
+ .mid = 0x32,
+ .reqs = {3, 4, 2}
+ },
+ { .vnid = VNID_MIC,
+ .mid = 0x37,
+ .reqs = {2, 3, 1}
+ }
+};
+
enum hda_cmd_vendor_io {
/* for DspIO node */
VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
@@ -698,11 +892,12 @@ enum dsp_download_state {
*/
struct ca0132_spec {
- struct snd_kcontrol_new *mixers[5];
+ const struct snd_kcontrol_new *mixers[5];
unsigned int num_mixers;
const struct hda_verb *base_init_verbs;
const struct hda_verb *base_exit_verbs;
const struct hda_verb *chip_init_verbs;
+ const struct hda_verb *sbz_init_verbs;
struct hda_verb *spec_init_verbs;
struct auto_pin_cfg autocfg;
@@ -719,6 +914,7 @@ struct ca0132_spec {
hda_nid_t shared_mic_nid;
hda_nid_t shared_out_nid;
hda_nid_t unsol_tag_hp;
+ hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */
hda_nid_t unsol_tag_amic1;
/* chip access */
@@ -734,6 +930,9 @@ struct ca0132_spec {
unsigned int scp_resp_header;
unsigned int scp_resp_data[4];
unsigned int scp_resp_count;
+ bool alt_firmware_present;
+ bool startup_check_entered;
+ bool dsp_reload;
/* mixer and effects related */
unsigned char dmic_ctl;
@@ -746,6 +945,17 @@ struct ca0132_spec {
long effects_switch[EFFECTS_COUNT];
long voicefx_val;
long cur_mic_boost;
+ /* ca0132_alt control related values */
+ unsigned char in_enum_val;
+ unsigned char out_enum_val;
+ unsigned char mic_boost_enum_val;
+ unsigned char smart_volume_setting;
+ long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
+ long xbass_xover_freq;
+ long eq_preset_val;
+ unsigned int tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+
struct hda_codec *codec;
struct delayed_work unsol_hp_work;
@@ -754,6 +964,25 @@ struct ca0132_spec {
#ifdef ENABLE_TUNING_CONTROLS
long cur_ctl_vals[TUNING_CTLS_COUNT];
#endif
+ /*
+ * Sound Blaster Z PCI region 2 iomem, used for input and output
+ * switching, and other unknown commands.
+ */
+ void __iomem *mem_base;
+
+ /*
+ * Whether or not to use the alt functions like alt_select_out,
+ * alt_select_in, etc. Only used on desktop codecs for now, because of
+ * surround sound support.
+ */
+ bool use_alt_functions;
+
+ /*
+ * Whether or not to use alt controls: volume effect sliders, EQ
+ * presets, smart volume presets, and new control names with FX prefix.
+ * Renames PlayEnhancement and CrystalVoice too.
+ */
+ bool use_alt_controls;
};
/*
@@ -762,6 +991,8 @@ struct ca0132_spec {
enum {
QUIRK_NONE,
QUIRK_ALIENWARE,
+ QUIRK_SBZ,
+ QUIRK_R3DI,
};
static const struct hda_pintbl alienware_pincfgs[] = {
@@ -778,10 +1009,44 @@ static const struct hda_pintbl alienware_pincfgs[] = {
{}
};
+/* Sound Blaster Z pin configs taken from Windows Driver */
+static const struct hda_pintbl sbz_pincfgs[] = {
+ { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c510f0 }, /* SPDIF In */
+ { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Recon3D integrated pin configs taken from Windows Driver */
+static const struct hda_pintbl r3di_pincfgs[] = {
+ { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x41c520f0 }, /* SPDIF In */
+ { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x500000f0 }, /* N/A */
+ {}
+};
+
static const struct snd_pci_quirk ca0132_quirks[] = {
SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1458, 0xA036, "Recon3Di", QUIRK_R3DI),
{}
};
@@ -965,6 +1230,29 @@ exit:
}
/*
+ * Write given value to the given address through the chip I/O widget.
+ * not protected by the Mutex
+ */
+static int chipio_write_no_mutex(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ int err;
+
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ return err;
+}
+
+/*
* Write multiple values to the given address through the chip I/O widget.
* protected by the Mutex
*/
@@ -1058,6 +1346,81 @@ static void chipio_set_control_param(struct hda_codec *codec,
}
/*
+ * Set chip parameters through the chip I/O widget. NO MUTEX.
+ */
+static void chipio_set_control_param_no_mutex(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ }
+}
+/*
+ * Connect stream to a source point, and then connect
+ * that source point to a destination point.
+ */
+static void chipio_set_stream_source_dest(struct hda_codec *codec,
+ int streamid, int source_point, int dest_point)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point);
+}
+
+/*
+ * Set number of channels in the selected stream.
+ */
+static void chipio_set_stream_channels(struct hda_codec *codec,
+ int streamid, unsigned int channels)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAMS_CHANNELS, channels);
+}
+
+/*
+ * Enable/Disable audio stream.
+ */
+static void chipio_set_stream_control(struct hda_codec *codec,
+ int streamid, int enable)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_CONTROL, enable);
+}
+
+
+/*
+ * Set sampling rate of the connection point. NO MUTEX.
+ */
+static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate);
+}
+
+/*
* Set sampling rate of the connection point.
*/
static void chipio_set_conn_rate(struct hda_codec *codec,
@@ -1420,8 +1783,8 @@ static int dspio_send_scp_message(struct hda_codec *codec,
* Returns zero or a negative error code.
*/
static int dspio_scp(struct hda_codec *codec,
- int mod_id, int req, int dir, void *data, unsigned int len,
- void *reply, unsigned int *reply_len)
+ int mod_id, int src_id, int req, int dir, const void *data,
+ unsigned int len, void *reply, unsigned int *reply_len)
{
int status = 0;
struct scp_msg scp_send, scp_reply;
@@ -1445,7 +1808,7 @@ static int dspio_scp(struct hda_codec *codec,
return -EINVAL;
}
- scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
+ scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req,
0, 0, 0, len/sizeof(unsigned int));
if (data != NULL && len > 0) {
len = min((unsigned int)(sizeof(scp_send.data)), len);
@@ -1502,15 +1865,24 @@ static int dspio_scp(struct hda_codec *codec,
* Set DSP parameters
*/
static int dspio_set_param(struct hda_codec *codec, int mod_id,
- int req, void *data, unsigned int len)
+ int src_id, int req, const void *data, unsigned int len)
{
- return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
+ return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL,
+ NULL);
}
static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
- int req, unsigned int data)
+ int req, const unsigned int data)
{
- return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int));
+ return dspio_set_param(codec, mod_id, 0x20, req, &data,
+ sizeof(unsigned int));
+}
+
+static int dspio_set_uint_param_no_source(struct hda_codec *codec, int mod_id,
+ int req, const unsigned int data)
+{
+ return dspio_set_param(codec, mod_id, 0x00, req, &data,
+ sizeof(unsigned int));
}
/*
@@ -1522,8 +1894,9 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
unsigned int size = sizeof(dma_chan);
codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n");
- status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
- SCP_GET, NULL, 0, dma_chan, &size);
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0,
+ dma_chan, &size);
if (status < 0) {
codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n");
@@ -1552,8 +1925,9 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
codec_dbg(codec, " dspio_free_dma_chan() -- begin\n");
codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan);
- status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
- SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan,
+ sizeof(dma_chan), NULL, &dummy);
if (status < 0) {
codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n");
@@ -2575,14 +2949,16 @@ exit:
*/
static void dspload_post_setup(struct hda_codec *codec)
{
+ struct ca0132_spec *spec = codec->spec;
codec_dbg(codec, "---- dspload_post_setup ------\n");
+ if (!spec->use_alt_functions) {
+ /*set DSP speaker to 2.0 configuration*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
- /*set DSP speaker to 2.0 configuration*/
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
-
- /*update write pointer*/
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+ /*update write pointer*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+ }
}
/**
@@ -2690,6 +3066,170 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
}
/*
+ * Setup GPIO for the other variants of Core3D.
+ */
+
+/*
+ * Sets up the GPIO pins so that they are discoverable. If this isn't done,
+ * the card shows as having no GPIO pins.
+ */
+static void ca0132_gpio_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+ snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B);
+ break;
+ }
+
+}
+
+/* Sets the GPIO for audio output. */
+static void ca0132_gpio_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x04);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x06);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x1E);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x1F);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x0C);
+ break;
+ }
+}
+
+/*
+ * GPIO control functions for the Recon3D integrated.
+ */
+
+enum r3di_gpio_bit {
+ /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */
+ R3DI_MIC_SELECT_BIT = 1,
+ /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */
+ R3DI_OUT_SELECT_BIT = 2,
+ /*
+ * I dunno what this actually does, but it stays on until the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADING = 3,
+ /*
+ * Same as above, no clue what it does, but it comes on after the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADED = 4
+};
+
+enum r3di_mic_select {
+ /* Set GPIO bit 1 to 0 for rear mic */
+ R3DI_REAR_MIC = 0,
+ /* Set GPIO bit 1 to 1 for front microphone*/
+ R3DI_FRONT_MIC = 1
+};
+
+enum r3di_out_select {
+ /* Set GPIO bit 2 to 0 for headphone */
+ R3DI_HEADPHONE_OUT = 0,
+ /* Set GPIO bit 2 to 1 for speaker */
+ R3DI_LINE_OUT = 1
+};
+enum r3di_dsp_status {
+ /* Set GPIO bit 3 to 1 until DSP is downloaded */
+ R3DI_DSP_DOWNLOADING = 0,
+ /* Set GPIO bit 4 to 1 once DSP is downloaded */
+ R3DI_DSP_DOWNLOADED = 1
+};
+
+
+static void r3di_gpio_mic_set(struct hda_codec *codec,
+ enum r3di_mic_select cur_mic)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (cur_mic) {
+ case R3DI_REAR_MIC:
+ cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT);
+ break;
+ case R3DI_FRONT_MIC:
+ cur_gpio |= (1 << R3DI_MIC_SELECT_BIT);
+ break;
+ }
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_out_set(struct hda_codec *codec,
+ enum r3di_out_select cur_out)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (cur_out) {
+ case R3DI_HEADPHONE_OUT:
+ cur_gpio &= ~(1 << R3DI_OUT_SELECT_BIT);
+ break;
+ case R3DI_LINE_OUT:
+ cur_gpio |= (1 << R3DI_OUT_SELECT_BIT);
+ break;
+ }
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
+ enum r3di_dsp_status dsp_status)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (dsp_status) {
+ case R3DI_DSP_DOWNLOADING:
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+ break;
+ case R3DI_DSP_DOWNLOADED:
+ /* Set DOWNLOADING bit to 0. */
+ cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED);
+ break;
+ }
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+/*
* PCM callbacks
*/
static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -2852,6 +3392,24 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
.tlv = { .c = ca0132_volume_tlv }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+/*
+ * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the
+ * volume put, which is used for setting the DSP volume. This was done because
+ * the ca0132 functions were taking too much time and causing lag.
+ */
+#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = snd_hda_mixer_amp_volume_info, \
+ .get = snd_hda_mixer_amp_volume_get, \
+ .put = ca0132_alt_volume_put, \
+ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -2864,9 +3422,88 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
/* stereo */
#define CA0132_CODEC_VOL(xname, nid, dir) \
CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \
+ CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir)
#define CA0132_CODEC_MUTE(xname, nid, dir) \
CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
+/* lookup tables */
+/*
+ * Lookup table with decibel values for the DSP. When volume is changed in
+ * Windows, the DSP is also sent the dB value in floating point. In Windows,
+ * these values have decimal points, probably because the Windows driver
+ * actually uses floating point. We can't here, so I made a lookup table of
+ * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the
+ * DAC's, and 9 is the maximum.
+ */
+static const unsigned int float_vol_db_lookup[] = {
+0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000,
+0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000,
+0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000,
+0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000,
+0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000,
+0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000,
+0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000,
+0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000,
+0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000,
+0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000,
+0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000,
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000
+};
+
+/*
+ * This table counts from float 0 to 1 in increments of .01, which is
+ * useful for a few different sliders.
+ */
+static const unsigned int float_zero_to_one_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+/*
+ * This table counts from float 10 to 1000, which is the range of the x-bass
+ * crossover slider in Windows.
+ */
+static const unsigned int float_xbass_xover_lookup[] = {
+0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000,
+0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000,
+0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000,
+0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000,
+0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000,
+0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000,
+0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000,
+0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000,
+0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000,
+0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000,
+0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000,
+0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000,
+0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000,
+0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000,
+0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000,
+0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000,
+0x44728000, 0x44750000, 0x44778000, 0x447A0000
+};
+
/* The following are for tuning of products */
#ifdef ENABLE_TUNING_CONTROLS
@@ -2942,7 +3579,7 @@ static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
break;
snd_hda_power_up(codec);
- dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20,
ca0132_tuning_ctls[i].req,
&(lookup[idx]), sizeof(unsigned int));
snd_hda_power_down(codec);
@@ -3068,8 +3705,8 @@ static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
-static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0);
static int add_tuning_control(struct hda_codec *codec,
hda_nid_t pnid, hda_nid_t nid,
@@ -3207,7 +3844,7 @@ static int ca0132_select_out(struct hda_codec *codec)
pin_ctl & ~PIN_HP);
/* enable speaker node */
pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
snd_hda_set_pin_ctl(codec, spec->out_pins[0],
pin_ctl | PIN_OUT);
} else {
@@ -3251,13 +3888,209 @@ exit:
return err < 0 ? err : 0;
}
+/*
+ * This function behaves similarly to the ca0132_select_out funciton above,
+ * except with a few differences. It adds the ability to select the current
+ * output with an enumerated control "output source" if the auto detect
+ * mute switch is set to off. If the auto detect mute switch is enabled, it
+ * will detect either headphone or lineout(SPEAKER_OUT) from jack detection.
+ * It also adds the ability to auto-detect the front headphone port. The only
+ * way to select surround is to disable auto detect, and set Surround with the
+ * enumerated control.
+ */
+static int ca0132_alt_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int pin_ctl;
+ int jack_present;
+ int auto_jack;
+ unsigned int i;
+ unsigned int tmp;
+ int err;
+ /* Default Headphone is rear headphone */
+ hda_nid_t headphone_nid = spec->out_pins[1];
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ snd_hda_power_up_pm(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ /*
+ * If headphone rear or front is plugged in, set to headphone.
+ * If neither is plugged in, set to rear line out. Only if
+ * hp/speaker auto detect is enabled.
+ */
+ if (auto_jack) {
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) ||
+ snd_hda_jack_detect(codec, spec->unsol_tag_front_hp);
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+ } else
+ spec->cur_out_type = spec->out_enum_val;
+
+ /* Begin DSP output switch */
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp);
+ if (err < 0)
+ goto exit;
+
+ switch (spec->cur_out_type) {
+ case SPEAKER_OUT:
+ codec_dbg(codec, "%s speaker\n", __func__);
+ /*speaker out config*/
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0007, spec->mem_base + 0x320);
+ writew(0x0104, spec->mem_base + 0x320);
+ writew(0x0101, spec->mem_base + 0x320);
+ chipio_set_control_param(codec, 0x0D, 0x18);
+ break;
+ case QUIRK_R3DI:
+ chipio_set_control_param(codec, 0x0D, 0x24);
+ r3di_gpio_out_set(codec, R3DI_LINE_OUT);
+ break;
+ }
+
+ /* disable headphone node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl & ~PIN_HP);
+ /* enable line-out node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl | PIN_OUT);
+ /* Enable EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+
+ /* If PlayEnhancement is enabled, set different source */
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+ else
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
+ break;
+ case HEADPHONE_OUT:
+ codec_dbg(codec, "%s hp\n", __func__);
+ /* Headphone out config*/
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0107, spec->mem_base + 0x320);
+ writew(0x0104, spec->mem_base + 0x320);
+ writew(0x0001, spec->mem_base + 0x320);
+ chipio_set_control_param(codec, 0x0D, 0x12);
+ break;
+ case QUIRK_R3DI:
+ chipio_set_control_param(codec, 0x0D, 0x21);
+ r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT);
+ break;
+ }
+
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ /* disable speaker*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl & ~PIN_HP);
+
+ /* enable headphone, either front or rear */
+
+ if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp))
+ headphone_nid = spec->out_pins[2];
+ else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp))
+ headphone_nid = spec->out_pins[1];
+
+ pin_ctl = snd_hda_codec_read(codec, headphone_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, headphone_nid,
+ pin_ctl | PIN_HP);
+
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+ else
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO);
+ break;
+ case SURROUND_OUT:
+ codec_dbg(codec, "%s surround\n", __func__);
+ /* Surround out config*/
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0007, spec->mem_base + 0x320);
+ writew(0x0104, spec->mem_base + 0x320);
+ writew(0x0101, spec->mem_base + 0x320);
+ chipio_set_control_param(codec, 0x0D, 0x18);
+ break;
+ case QUIRK_R3DI:
+ chipio_set_control_param(codec, 0x0D, 0x24);
+ r3di_gpio_out_set(codec, R3DI_LINE_OUT);
+ break;
+ }
+ /* enable line out node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl | PIN_OUT);
+ /* Disable headphone out */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl & ~PIN_HP);
+ /* Enable EAPD on line out */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+ /* enable center/lfe out node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[2],
+ pin_ctl | PIN_OUT);
+ /* Now set rear surround node as out. */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[3],
+ pin_ctl | PIN_OUT);
+
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+ else
+ dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
+ break;
+ }
+
+ /* run through the output dsp commands for line-out */
+ for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) {
+ err = dspio_set_uint_param(codec,
+ alt_out_presets[spec->cur_out_type].mids[i],
+ alt_out_presets[spec->cur_out_type].reqs[i],
+ alt_out_presets[spec->cur_out_type].vals[i]);
+
+ if (err < 0)
+ goto exit;
+ }
+
+exit:
+ snd_hda_power_down_pm(codec);
+
+ return err < 0 ? err : 0;
+}
+
static void ca0132_unsol_hp_delayed(struct work_struct *work)
{
struct ca0132_spec *spec = container_of(
to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
struct hda_jack_tbl *jack;
- ca0132_select_out(spec->codec);
+ if (spec->use_alt_functions)
+ ca0132_alt_select_out(spec->codec);
+ else
+ ca0132_select_out(spec->codec);
+
jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
if (jack) {
jack->block_report = 0;
@@ -3268,6 +4101,10 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work)
static void ca0132_set_dmic(struct hda_codec *codec, int enable);
static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval);
+static int stop_mic1(struct hda_codec *codec);
+static int ca0132_cvoice_switch_set(struct hda_codec *codec);
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val);
/*
* Select the active VIP source
@@ -3310,6 +4147,71 @@ static int ca0132_set_vipsource(struct hda_codec *codec, int val)
return 1;
}
+static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* if CrystalVoice is off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0) || spec->in_enum_val == REAR_LINE_IN) {
+ codec_dbg(codec, "%s: off.", __func__);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->quirk == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+
+ if (spec->in_enum_val == REAR_LINE_IN)
+ tmp = FLOAT_ZERO;
+ else {
+ if (spec->quirk == QUIRK_SBZ)
+ tmp = FLOAT_THREE;
+ else
+ tmp = FLOAT_ONE;
+ }
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ } else {
+ codec_dbg(codec, "%s: on.", __func__);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (spec->quirk == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_16_000);
+
+ if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID])
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ return 1;
+}
+
/*
* Select the active microphone.
* If autodetect is enabled, mic will be selected based on jack detection.
@@ -3363,6 +4265,125 @@ static int ca0132_select_mic(struct hda_codec *codec)
}
/*
+ * Select the active input.
+ * Mic detection isn't used, because it's kind of pointless on the SBZ.
+ * The front mic has no jack-detection, so the only way to switch to it
+ * is to do it manually in alsamixer.
+ */
+static int ca0132_alt_select_in(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ snd_hda_power_up_pm(codec);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ spec->cur_mic_type = spec->in_enum_val;
+
+ switch (spec->cur_mic_type) {
+ case REAR_MIC:
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0000, spec->mem_base + 0x320);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->quirk == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ if (spec->quirk == QUIRK_SBZ) {
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000000C);
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ case REAR_LINE_IN:
+ ca0132_mic_boost_set(codec, 0);
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0000, spec->mem_base + 0x320);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->quirk == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ if (spec->quirk == QUIRK_SBZ) {
+ chipio_write(codec, 0x18B098, 0x00000000);
+ chipio_write(codec, 0x18B09C, 0x00000000);
+ }
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+ break;
+ case FRONT_MIC:
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ writew(0x0100, spec->mem_base + 0x320);
+ writew(0x0005, spec->mem_base + 0x320);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_FRONT_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->quirk == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ if (spec->quirk == QUIRK_SBZ) {
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x000000CC);
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ }
+ ca0132_cvoice_switch_set(codec);
+
+ snd_hda_power_down_pm(codec);
+ return 0;
+
+}
+
+/*
* Check if VNODE settings take effect immediately.
*/
static bool ca0132_is_vnode_effective(struct hda_codec *codec,
@@ -3418,7 +4439,7 @@ static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
{
struct ca0132_spec *spec = codec->spec;
- unsigned int on;
+ unsigned int on, tmp;
int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
int err = 0;
int idx = nid - EFFECT_START_NID;
@@ -3442,6 +4463,46 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
/* Voice Focus applies to 2-ch Mic, Digital Mic */
if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
val = 0;
+
+ /* If Voice Focus on SBZ, set to two channel. */
+ if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+
+ if (spec->effects_switch[VOICE_FOCUS -
+ EFFECT_START_NID]) {
+ tmp = FLOAT_TWO;
+ val = 1;
+ } else
+ tmp = FLOAT_ONE;
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ }
+ }
+ /*
+ * For SBZ noise reduction, there's an extra command
+ * to module ID 0x47. No clue why.
+ */
+ if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+ if (spec->effects_switch[NOISE_REDUCTION -
+ EFFECT_START_NID])
+ tmp = FLOAT_ONE;
+ else
+ tmp = FLOAT_ZERO;
+ } else
+ tmp = FLOAT_ZERO;
+
+ dspio_set_uint_param(codec, 0x47, 0x00, tmp);
+ }
+
+ /* If rear line in disable effects. */
+ if (spec->use_alt_functions &&
+ spec->in_enum_val == REAR_LINE_IN)
+ val = 0;
}
codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
@@ -3469,6 +4530,9 @@ static int ca0132_pe_switch_set(struct hda_codec *codec)
codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
+ if (spec->use_alt_functions)
+ ca0132_alt_select_out(codec);
+
i = OUT_EFFECT_START_NID - EFFECT_START_NID;
nid = OUT_EFFECT_START_NID;
/* PE affects all out effects */
@@ -3526,7 +4590,10 @@ static int ca0132_cvoice_switch_set(struct hda_codec *codec)
/* set correct vipsource */
oldval = stop_mic1(codec);
- ret |= ca0132_set_vipsource(codec, 1);
+ if (spec->use_alt_functions)
+ ret |= ca0132_alt_set_vipsource(codec, 1);
+ else
+ ret |= ca0132_set_vipsource(codec, 1);
resume_mic1(codec, oldval);
return ret;
}
@@ -3546,6 +4613,16 @@ static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
return ret;
}
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, val);
+ return ret;
+}
+
static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -3560,8 +4637,12 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
if (nid == VNID_HP_SEL) {
auto_jack =
spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
- if (!auto_jack)
- ca0132_select_out(codec);
+ if (!auto_jack) {
+ if (spec->use_alt_functions)
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
+ }
return 1;
}
@@ -3574,7 +4655,10 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
}
if (nid == VNID_HP_ASEL) {
- ca0132_select_out(codec);
+ if (spec->use_alt_functions)
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
return 1;
}
@@ -3602,6 +4686,432 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
return ret;
}
/* End of control change helpers. */
+/*
+ * Below I've added controls to mess with the effect levels, I've only enabled
+ * them on the Sound Blaster Z, but they would probably also work on the
+ * Chromebook. I figured they were probably tuned specifically for it, and left
+ * out for a reason.
+ */
+
+/* Sets DSP effect level from the sliders above the controls */
+static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ const unsigned int *lookup, int idx)
+{
+ int i = 0;
+ unsigned int y;
+ /*
+ * For X_BASS, req 2 is actually crossover freq instead of
+ * effect level
+ */
+ if (nid == X_BASS)
+ y = 2;
+ else
+ y = 1;
+
+ snd_hda_power_up(codec);
+ if (nid == XBASS_XOVER) {
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (ca0132_effects[i].nid == X_BASS)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[1],
+ &(lookup[idx - 1]), sizeof(unsigned int));
+ } else {
+ /* Find the actual effect structure */
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (nid == ca0132_effects[i].nid)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[y],
+ &(lookup[idx]), sizeof(unsigned int));
+ }
+
+ snd_hda_power_down(codec);
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+
+ *valp = spec->xbass_xover_freq;
+ return 0;
+}
+
+static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - OUT_EFFECT_START_NID;
+
+ *valp = spec->fx_ctl_val[idx];
+ return 0;
+}
+
+/*
+ * The X-bass crossover starts at 10hz, so the min is 1. The
+ * frequency is set in multiples of 10.
+ */
+static int ca0132_alt_xbass_xover_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ /* any change? */
+ if (spec->xbass_xover_freq == *valp)
+ return 0;
+
+ spec->xbass_xover_freq = *valp;
+
+ idx = *valp;
+ ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - EFFECT_START_NID;
+ /* any change? */
+ if (spec->fx_ctl_val[idx] == *valp)
+ return 0;
+
+ spec->fx_ctl_val[idx] = *valp;
+
+ idx = *valp;
+ ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx);
+
+ return 0;
+}
+
+
+/*
+ * Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original
+ * only has off or full 30 dB, and didn't like making a volume slider that has
+ * traditional 0-100 in alsamixer that goes in big steps. I like enum better.
+ */
+#define MIC_BOOST_NUM_OF_STEPS 4
+#define MIC_BOOST_ENUM_MAX_STRLEN 10
+
+static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char *sfx = "dB";
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS;
+ if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS)
+ uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1;
+ sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx);
+ strcpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = MIC_BOOST_NUM_OF_STEPS;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n",
+ sel);
+
+ spec->mic_boost_enum_val = sel;
+
+ if (spec->in_enum_val != REAR_LINE_IN)
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+
+ return 1;
+}
+
+
+/*
+ * Input Select Control for alternative ca0132 codecs. This exists because
+ * front microphone has no auto-detect, and we need a way to set the rear
+ * as line-in
+ */
+static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS;
+ if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS)
+ uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ in_src_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->in_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = IN_SRC_NUM_OF_INPUTS;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n",
+ sel, in_src_str[sel]);
+
+ spec->in_enum_val = sel;
+
+ ca0132_alt_select_in(codec);
+
+ return 1;
+}
+
+/* Sound Blaster Z Output Select Control */
+static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_OUTPUTS;
+ if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS)
+ uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ alt_out_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->out_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_OUTPUTS;
+ unsigned int auto_jack;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n",
+ sel, alt_out_presets[sel].name);
+
+ spec->out_enum_val = sel;
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (!auto_jack)
+ ca0132_alt_select_out(codec);
+
+ return 1;
+}
+
+/*
+ * Smart Volume output setting control. Three different settings, Normal,
+ * which takes the value from the smart volume slider. The two others, loud
+ * and night, disregard the slider value and have uneditable values.
+ */
+#define NUM_OF_SVM_SETTINGS 3
+static const char *const out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" };
+
+static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS;
+ if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS)
+ uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1;
+ strcpy(uinfo->value.enumerated.name,
+ out_svm_set_enum_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->smart_volume_setting;
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_SVM_SETTINGS;
+ unsigned int idx = SMART_VOLUME - EFFECT_START_NID;
+ unsigned int tmp;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n",
+ sel, out_svm_set_enum_str[sel]);
+
+ spec->smart_volume_setting = sel;
+
+ switch (sel) {
+ case 0:
+ tmp = FLOAT_ZERO;
+ break;
+ case 1:
+ tmp = FLOAT_ONE;
+ break;
+ case 2:
+ tmp = FLOAT_TWO;
+ break;
+ default:
+ tmp = FLOAT_ZERO;
+ break;
+ }
+ /* Req 2 is the Smart Volume Setting req. */
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[2], tmp);
+ return 1;
+}
+
+/* Sound Blaster Z EQ preset controls */
+static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ ca0132_alt_eq_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->eq_preset_val;
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, sel,
+ ca0132_alt_eq_presets[sel].name);
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid,
+ ca0132_alt_eq_enum.reqs[i],
+ ca0132_alt_eq_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0)
+ spec->eq_preset_val = sel;
+
+ return 1;
+}
static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -3753,10 +5263,15 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
/* mic boost */
if (nid == spec->input_pins[0]) {
spec->cur_mic_boost = *valp;
+ if (spec->use_alt_functions) {
+ if (spec->in_enum_val != REAR_LINE_IN)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ } else {
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ changed = ca0132_mic_boost_set(codec, *valp);
+ }
- /* Mic boost does not apply to Digital Mic */
- if (spec->cur_mic_type != DIGITAL_MIC)
- changed = ca0132_mic_boost_set(codec, *valp);
goto exit;
}
@@ -3768,6 +5283,41 @@ exit:
/*
* Volume related
*/
+/*
+ * Sets the internal DSP decibel level to match the DAC for output, and the
+ * ADC for input. Currently only the SBZ sets dsp capture volume level, and
+ * all alternative codecs set DSP playback volume.
+ */
+static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_dir;
+ unsigned int lookup_val;
+
+ if (nid == VNID_SPK)
+ dsp_dir = DSP_VOL_OUT;
+ else
+ dsp_dir = DSP_VOL_IN;
+
+ lookup_val = spec->vnode_lvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[0],
+ float_vol_db_lookup[lookup_val]);
+
+ lookup_val = spec->vnode_rvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[1],
+ float_vol_db_lookup[lookup_val]);
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO);
+}
+
static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -3869,6 +5419,51 @@ static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
return changed;
}
+/*
+ * This function is the same as the one above, because using an if statement
+ * inside of the above volume control for the DSP volume would cause too much
+ * lag. This is a lot more smooth.
+ */
+static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t vnid = 0;
+ int changed = 1;
+
+ switch (nid) {
+ case 0x02:
+ vnid = VNID_SPK;
+ break;
+ case 0x07:
+ vnid = VNID_MIC;
+ break;
+ }
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ snd_hda_power_up(codec);
+ ca0132_alt_dsp_volume_put(codec, vnid);
+ mutex_lock(&codec->control_mutex);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ mutex_unlock(&codec->control_mutex);
+ snd_hda_power_down(codec);
+
+ return changed;
+}
+
static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
@@ -3907,14 +5502,59 @@ static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return err;
}
+/* Add volume slider control for effect level */
+static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]);
+
+ knew.tlv.c = 0;
+ knew.tlv.p = 0;
+
+ switch (nid) {
+ case XBASS_XOVER:
+ knew.info = ca0132_alt_xbass_xover_slider_info;
+ knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+ knew.put = ca0132_alt_xbass_xover_slider_put;
+ break;
+ default:
+ knew.info = ca0132_alt_effect_slider_info;
+ knew.get = ca0132_alt_slider_ctl_get;
+ knew.put = ca0132_alt_effect_slider_put;
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ break;
+ }
+
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Added FX: prefix for the alternative codecs, because otherwise the surround
+ * effect would conflict with the Surround sound volume control. Also seems more
+ * clear as to what the switches do. Left alone for others.
+ */
static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
const char *pfx, int dir)
{
+ struct ca0132_spec *spec = codec->spec;
char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ /* If using alt_controls, add FX: prefix. But, don't add FX:
+ * prefix to OutFX or InFX enable controls.
+ */
+ if ((spec->use_alt_controls) && (nid <= IN_EFFECT_END_NID))
+ sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]);
+ else
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
@@ -3929,11 +5569,141 @@ static int add_voicefx(struct hda_codec *codec)
return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
}
+/* Create the EQ Preset control */
+static int add_ca0132_alt_eq_presets(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name,
+ EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_eq_preset_info;
+ knew.get = ca0132_alt_eq_preset_get;
+ knew.put = ca0132_alt_eq_preset_put;
+ return snd_hda_ctl_add(codec, EQ_PRESET_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add enumerated control for the three different settings of the smart volume
+ * output effect. Normal just uses the slider value, and loud and night are
+ * their own things that ignore that value.
+ */
+static int ca0132_alt_add_svm_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting",
+ SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_svm_setting_info;
+ knew.get = ca0132_alt_svm_setting_get;
+ knew.put = ca0132_alt_svm_setting_put;
+ return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Create an Output Select enumerated control for codecs with surround
+ * out capabilities.
+ */
+static int ca0132_alt_add_output_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Output Select",
+ OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_output_select_get_info;
+ knew.get = ca0132_alt_output_select_get;
+ knew.put = ca0132_alt_output_select_put;
+ return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Create an Input Source enumerated control for the alternate ca0132 codecs
+ * because the front microphone has no auto-detect, and Line-in has to be set
+ * somehow.
+ */
+static int ca0132_alt_add_input_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Input Source",
+ INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_input_source_info;
+ knew.get = ca0132_alt_input_source_get;
+ knew.put = ca0132_alt_input_source_put;
+ return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds
+ * more control than the original mic boost, which is either full 30dB or off.
+ */
+static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch",
+ MIC_BOOST_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_mic_boost_info;
+ knew.get = ca0132_alt_mic_boost_get;
+ knew.put = ca0132_alt_mic_boost_put;
+ return snd_hda_ctl_add(codec, MIC_BOOST_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Need to create slave controls for the alternate codecs that have surround
+ * capabilities.
+ */
+static const char * const ca0132_alt_slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", NULL,
+};
+
+/*
+ * Also need special channel map, because the default one is incorrect.
+ * I think this has to do with the pin for rear surround being 0x11,
+ * and the center/lfe being 0x10. Usually the pin order is the opposite.
+ */
+const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+/* Add the correct chmap for streams with 6 channels. */
+static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec)
+{
+ int err = 0;
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ struct hda_pcm_stream *hinfo =
+ &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
+
+ elem = ca0132_alt_chmaps;
+ if (hinfo->channels_max == 6) {
+ err = snd_pcm_add_chmap_ctls(pcm->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ elem, hinfo->channels_max, 0, &chmap);
+ if (err < 0)
+ codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!");
+ }
+ }
+}
+
/*
* When changing Node IDs for Mixer Controls below, make sure to update
* Node IDs in ca0132_config() as well.
*/
-static struct snd_kcontrol_new ca0132_mixer[] = {
+static const struct snd_kcontrol_new ca0132_mixer[] = {
CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
@@ -3955,10 +5725,55 @@ static struct snd_kcontrol_new ca0132_mixer[] = {
{ } /* end */
};
+/*
+ * SBZ specific control mixer. Removes auto-detect for mic, and adds surround
+ * controls. Also sets both the Front Playback and Capture Volume controls to
+ * alt so they set the DSP's decibel level.
+ */
+static const struct snd_kcontrol_new sbz_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
+/*
+ * Same as the Sound Blaster Z, except doesn't use the alt volume for capture
+ * because it doesn't set decibel levels for the DSP for capture.
+ */
+static const struct snd_kcontrol_new r3di_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
static int ca0132_build_controls(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
- int i, num_fx;
+ int i, num_fx, num_sliders;
int err = 0;
/* Add Mixer controls */
@@ -3967,29 +5782,94 @@ static int ca0132_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+ /* Setup vmaster with surround slaves for desktop ca0132 devices */
+ if (spec->use_alt_functions) {
+ snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT,
+ spec->tlv);
+ snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->tlv, ca0132_alt_slave_pfxs,
+ "Playback Volume");
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, ca0132_alt_slave_pfxs,
+ "Playback Switch",
+ true, &spec->vmaster_mute.sw_kctl);
+
+ }
/* Add in and out effects controls.
* VoiceFX, PE and CrystalVoice are added separately.
*/
num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
for (i = 0; i < num_fx; i++) {
+ /* SBZ breaks if Echo Cancellation is used */
+ if (spec->quirk == QUIRK_SBZ) {
+ if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID +
+ OUT_EFFECTS_COUNT))
+ continue;
+ }
+
err = add_fx_switch(codec, ca0132_effects[i].nid,
ca0132_effects[i].name,
ca0132_effects[i].direct);
if (err < 0)
return err;
}
+ /*
+ * If codec has use_alt_controls set to true, add effect level sliders,
+ * EQ presets, and Smart Volume presets. Also, change names to add FX
+ * prefix, and change PlayEnhancement and CrystalVoice to match.
+ */
+ if (spec->use_alt_controls) {
+ ca0132_alt_add_svm_enum(codec);
+ add_ca0132_alt_eq_presets(codec);
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "Enable OutFX", 0);
+ if (err < 0)
+ return err;
- err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
- if (err < 0)
- return err;
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "Enable InFX", 1);
+ if (err < 0)
+ return err;
- err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
- if (err < 0)
- return err;
+ num_sliders = OUT_EFFECTS_COUNT - 1;
+ for (i = 0; i < num_sliders; i++) {
+ err = ca0132_alt_add_effect_slider(codec,
+ ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER,
+ "X-Bass Crossover", EFX_DIR_OUT);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "CrystalVoice", 1);
+ if (err < 0)
+ return err;
+ }
add_voicefx(codec);
+ /*
+ * If the codec uses alt_functions, you need the enumerated controls
+ * to select the new outputs and inputs, plus add the new mic boost
+ * setting control.
+ */
+ if (spec->use_alt_functions) {
+ ca0132_alt_add_output_enum(codec);
+ ca0132_alt_add_input_enum(codec);
+ ca0132_alt_add_mic_boost_enum(codec);
+ }
#ifdef ENABLE_TUNING_CONTROLS
add_tuning_ctls(codec);
#endif
@@ -4014,6 +5894,10 @@ static int ca0132_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+
+ if (spec->use_alt_functions)
+ ca0132_alt_add_chmap_ctls(codec);
+
return 0;
}
@@ -4068,6 +5952,11 @@ static int ca0132_build_pcms(struct hda_codec *codec)
info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
if (!info)
return -ENOMEM;
+ if (spec->use_alt_functions) {
+ info->own_chmap = true;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap
+ = ca0132_alt_chmaps;
+ }
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
@@ -4076,12 +5965,16 @@ static int ca0132_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
- if (!info)
- return -ENOMEM;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ /* With the DSP enabled, desktops don't use this ADC. */
+ if (spec->use_alt_functions) {
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ }
info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
if (!info)
@@ -4288,6 +6181,196 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec)
}
/*
+ * Recon3Di r3di_setup_defaults sub functions.
+ */
+
+static void r3di_dsp_scp_startup(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ tmp = 0x00000000;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp);
+
+ tmp = 0x00000001;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp);
+
+ tmp = 0x00000004;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+ tmp = 0x00000005;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+ tmp = 0x00000000;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+}
+
+static void r3di_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ /* Mic 1 Setup */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ /* This ConnPointID is unique to Recon3Di. Haven't seen it elsewhere */
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ /* Mic 2 Setup, even though it isn't connected on SBZ */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+}
+
+/*
+ * Initialize Sound Blaster Z analog microphones.
+ */
+static void sbz_init_analog_mics(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ /* Mic 1 Setup */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ /* Mic 2 Setup, even though it isn't connected on SBZ */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+}
+
+/*
+ * Sets the source of stream 0x14 to connpointID 0x48, and the destination
+ * connpointID to 0x91. If this isn't done, the destination is 0x71, and
+ * you get no sound. I'm guessing this has to do with the Sound Blaster Z
+ * having an updated DAC, which changes the destination to that DAC.
+ */
+static void sbz_connect_streams(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n");
+
+ chipio_set_stream_channels(codec, 0x0C, 6);
+ chipio_set_stream_control(codec, 0x0C, 1);
+
+ /* This value is 0x43 for 96khz, and 0x83 for 192khz. */
+ chipio_write_no_mutex(codec, 0x18a020, 0x00000043);
+
+ /* Setup stream 0x14 with it's source and destination points */
+ chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91);
+ chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000);
+ chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000);
+ chipio_set_stream_channels(codec, 0x14, 2);
+ chipio_set_stream_control(codec, 0x14, 1);
+
+ codec_dbg(codec, "Connect Streams exited, mutex released.\n");
+
+ mutex_unlock(&spec->chipio_mutex);
+
+}
+
+/*
+ * Write data through ChipIO to setup proper stream destinations.
+ * Not sure how it exactly works, but it seems to direct data
+ * to different destinations. Example is f8 to c0, e0 to c0.
+ * All I know is, if you don't set these, you get no sound.
+ */
+static void sbz_chipio_startup_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_lock(&spec->chipio_mutex);
+ codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n");
+
+ /* These control audio output */
+ chipio_write_no_mutex(codec, 0x190060, 0x0001f8c0);
+ chipio_write_no_mutex(codec, 0x190064, 0x0001f9c1);
+ chipio_write_no_mutex(codec, 0x190068, 0x0001fac6);
+ chipio_write_no_mutex(codec, 0x19006c, 0x0001fbc7);
+ /* Signal to update I think */
+ chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+
+ chipio_set_stream_channels(codec, 0x0C, 6);
+ chipio_set_stream_control(codec, 0x0C, 1);
+ /* No clue what these control */
+ chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0);
+ chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1);
+ chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2);
+ chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3);
+ chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4);
+ chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5);
+ chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6);
+ chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7);
+ chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8);
+ chipio_write_no_mutex(codec, 0x190054, 0x0001edc9);
+ chipio_write_no_mutex(codec, 0x190058, 0x0001eaca);
+ chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb);
+
+ chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+
+ codec_dbg(codec, "Startup Data exited, mutex released.\n");
+ mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * Sound Blaster Z uses these after DSP is loaded. Weird SCP commands
+ * without a 0x20 source like normal.
+ */
+static void sbz_dsp_scp_startup(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ tmp = 0x00000003;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+ tmp = 0x00000000;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp);
+
+ tmp = 0x00000001;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp);
+
+ tmp = 0x00000004;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+ tmp = 0x00000005;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+ tmp = 0x00000000;
+ dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+}
+
+static void sbz_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+ unsigned int tmp;
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ chipio_write(codec, 0x18b098, 0x0000000c);
+ chipio_write(codec, 0x18b09C, 0x0000000c);
+}
+
+/*
* Setup default parameters for DSP
*/
static void ca0132_setup_defaults(struct hda_codec *codec)
@@ -4332,16 +6415,159 @@ static void ca0132_setup_defaults(struct hda_codec *codec)
}
/*
+ * Setup default parameters for Recon3Di DSP.
+ */
+
+static void r3di_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ r3di_dsp_scp_startup(codec);
+
+ r3di_dsp_initial_mic_setup(codec);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED);
+
+ /* Setup effect defaults */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+}
+
+/*
+ * Setup default parameters for the Sound Blaster Z DSP. A lot more going on
+ * than the Chromebook setup.
+ */
+static void sbz_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp, stream_format;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ sbz_dsp_scp_startup(codec);
+
+ sbz_init_analog_mics(codec);
+
+ sbz_connect_streams(codec);
+
+ sbz_chipio_startup_data(codec);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ /*
+ * Sets internal input loopback to off, used to have a switch to
+ * enable input loopback, but turned out to be way too buggy.
+ */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ sbz_dsp_initial_mic_setup(codec);
+
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ /*
+ * Have to make a stream to bind the sound output to, otherwise
+ * you'll get dead audio. Before I did this, it would bind to an
+ * audio input, and would never work
+ */
+ stream_format = snd_hdac_calc_stream_format(48000, 2,
+ SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id,
+ 0, stream_format);
+
+ snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id,
+ 0, stream_format);
+
+ snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+}
+
+/*
* Initialization of flags in chip
*/
static void ca0132_init_flags(struct hda_codec *codec)
{
- chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->use_alt_functions) {
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1);
+ } else {
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+ }
}
/*
@@ -4349,6 +6575,16 @@ static void ca0132_init_flags(struct hda_codec *codec)
*/
static void ca0132_init_params(struct hda_codec *codec)
{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->use_alt_functions) {
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+ chipio_set_conn_rate(codec, 0x0B, SR_48_000);
+ chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0);
+ chipio_set_control_param(codec, 0, 0);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ }
+
chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
}
@@ -4370,11 +6606,49 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
static bool ca0132_download_dsp_images(struct hda_codec *codec)
{
bool dsp_loaded = false;
+ struct ca0132_spec *spec = codec->spec;
const struct dsp_image_seg *dsp_os_image;
const struct firmware *fw_entry;
-
- if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0)
- return false;
+ /*
+ * Alternate firmwares for different variants. The Recon3Di apparently
+ * can use the default firmware, but I'll leave the option in case
+ * it needs it again.
+ */
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ if (request_firmware(&fw_entry, SBZ_EFX_FILE,
+ codec->card->dev) != 0) {
+ codec_dbg(codec, "SBZ alt firmware not detected. ");
+ spec->alt_firmware_present = false;
+ } else {
+ codec_dbg(codec, "Sound Blaster Z firmware selected.");
+ spec->alt_firmware_present = true;
+ }
+ break;
+ case QUIRK_R3DI:
+ if (request_firmware(&fw_entry, R3DI_EFX_FILE,
+ codec->card->dev) != 0) {
+ codec_dbg(codec, "Recon3Di alt firmware not detected.");
+ spec->alt_firmware_present = false;
+ } else {
+ codec_dbg(codec, "Recon3Di firmware selected.");
+ spec->alt_firmware_present = true;
+ }
+ break;
+ default:
+ spec->alt_firmware_present = false;
+ break;
+ }
+ /*
+ * Use default ctefx.bin if no alt firmware is detected, or if none
+ * exists for your particular codec.
+ */
+ if (!spec->alt_firmware_present) {
+ codec_dbg(codec, "Default firmware selected.");
+ if (request_firmware(&fw_entry, EFX_FILE,
+ codec->card->dev) != 0)
+ return false;
+ }
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
@@ -4402,13 +6676,17 @@ static void ca0132_download_dsp(struct hda_codec *codec)
return; /* don't retry failures */
chipio_enable_clocks(codec);
- spec->dsp_state = DSP_DOWNLOADING;
- if (!ca0132_download_dsp_images(codec))
- spec->dsp_state = DSP_DOWNLOAD_FAILED;
- else
- spec->dsp_state = DSP_DOWNLOADED;
+ if (spec->dsp_state != DSP_DOWNLOADED) {
+ spec->dsp_state = DSP_DOWNLOADING;
- if (spec->dsp_state == DSP_DOWNLOADED)
+ if (!ca0132_download_dsp_images(codec))
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
+ else
+ spec->dsp_state = DSP_DOWNLOADED;
+ }
+
+ /* For codecs using alt functions, this is already done earlier */
+ if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions))
ca0132_set_dsp_msr(codec, true);
}
@@ -4454,6 +6732,10 @@ static void ca0132_init_unsol(struct hda_codec *codec)
amic_callback);
snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
ca0132_process_dsp_response);
+ /* Front headphone jack detection */
+ if (spec->use_alt_functions)
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->unsol_tag_front_hp, hp_callback);
}
/*
@@ -4476,7 +6758,8 @@ static struct hda_verb ca0132_base_exit_verbs[] = {
{}
};
-/* Other verbs tables. Sends after DSP download. */
+/* Other verbs tables. Sends after DSP download. */
+
static struct hda_verb ca0132_init_verbs0[] = {
/* chip init verbs */
{0x15, 0x70D, 0xF0},
@@ -4506,8 +6789,27 @@ static struct hda_verb ca0132_init_verbs0[] = {
{0x15, 0x546, 0xC9},
{0x15, 0x53B, 0xCE},
{0x15, 0x5E8, 0xC9},
- {0x15, 0x717, 0x0D},
- {0x15, 0x718, 0x20},
+ {}
+};
+
+/* Extra init verbs for SBZ */
+static struct hda_verb sbz_init_verbs[] = {
+ {0x15, 0x70D, 0x20},
+ {0x15, 0x70E, 0x19},
+ {0x15, 0x707, 0x00},
+ {0x15, 0x539, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x70D, 0xB7},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x10},
+ {0x15, 0x70D, 0xAF},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x01},
+ {0x15, 0x707, 0x05},
+ {0x15, 0x70D, 0x73},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x14},
+ {0x15, 0x6FF, 0xC4},
{}
};
@@ -4521,7 +6823,11 @@ static void ca0132_init_chip(struct hda_codec *codec)
mutex_init(&spec->chipio_mutex);
spec->cur_out_type = SPEAKER_OUT;
- spec->cur_mic_type = DIGITAL_MIC;
+ if (!spec->use_alt_functions)
+ spec->cur_mic_type = DIGITAL_MIC;
+ else
+ spec->cur_mic_type = REAR_MIC;
+
spec->cur_mic_boost = 0;
for (i = 0; i < VNODES_COUNT; i++) {
@@ -4539,6 +6845,15 @@ static void ca0132_init_chip(struct hda_codec *codec)
on = (unsigned int)ca0132_effects[i].reqs[0];
spec->effects_switch[i] = on ? 1 : 0;
}
+ /*
+ * Sets defaults for the effect slider controls, only for alternative
+ * ca0132 codecs. Also sets x-bass crossover frequency to 80hz.
+ */
+ if (spec->use_alt_controls) {
+ spec->xbass_xover_freq = 8;
+ for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++)
+ spec->fx_ctl_val[i] = effect_slider_defaults[i];
+ }
spec->voicefx_val = 0;
spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
@@ -4549,6 +6864,120 @@ static void ca0132_init_chip(struct hda_codec *codec)
#endif
}
+/*
+ * Recon3Di exit specific commands.
+ */
+/* prevents popping noise on shutdown */
+static void r3di_gpio_shutdown(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00);
+}
+
+/*
+ * Sound Blaster Z exit specific commands.
+ */
+static void sbz_region2_exit(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ writeb(0x0, spec->mem_base + 0x100);
+ for (i = 0; i < 8; i++)
+ writeb(0xb3, spec->mem_base + 0x304);
+ /*
+ * I believe these are GPIO, with the right most hex digit being the
+ * gpio pin, and the second digit being on or off. We see this more in
+ * the input/output select functions.
+ */
+ writew(0x0000, spec->mem_base + 0x320);
+ writew(0x0001, spec->mem_base + 0x320);
+ writew(0x0104, spec->mem_base + 0x320);
+ writew(0x0005, spec->mem_base + 0x320);
+ writew(0x0007, spec->mem_base + 0x320);
+}
+
+static void sbz_set_pin_ctl_default(struct hda_codec *codec)
+{
+ hda_nid_t pins[5] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
+ unsigned int i;
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+ for (i = 0; i < 5; i++)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+}
+
+static void sbz_clear_unsolicited(struct hda_codec *codec)
+{
+ hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
+ unsigned int i;
+
+ for (i = 0; i < 7; i++) {
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0x00);
+ }
+}
+
+/* On shutdown, sends commands in sets of three */
+static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir,
+ int mask, int data)
+{
+ if (dir >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, dir);
+ if (mask >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, mask);
+
+ if (data >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, data);
+}
+
+static void sbz_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* Mess with GPIO */
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01);
+
+ chipio_set_stream_control(codec, 0x14, 0);
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_conn_rate(codec, 0x41, SR_192_000);
+ chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+ chipio_write(codec, 0x18a020, 0x00000083);
+
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06);
+
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_control_param(codec, 0x0D, 0x24);
+
+ sbz_clear_unsolicited(codec);
+ sbz_set_pin_ctl_default(codec);
+
+ snd_hda_codec_write(codec, 0x0B, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ if (dspload_is_loaded(codec))
+ dsp_reset(codec);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x00);
+
+ sbz_region2_exit(codec);
+}
+
static void ca0132_exit_chip(struct hda_codec *codec)
{
/* put any chip cleanup stuffs here. */
@@ -4557,28 +6986,264 @@ static void ca0132_exit_chip(struct hda_codec *codec)
dsp_reset(codec);
}
+/*
+ * This fixes a problem that was hard to reproduce. Very rarely, I would
+ * boot up, and there would be no sound, but the DSP indicated it had loaded
+ * properly. I did a few memory dumps to see if anything was different, and
+ * there were a few areas of memory uninitialized with a1a2a3a4. This function
+ * checks if those areas are uninitialized, and if they are, it'll attempt to
+ * reload the card 3 times. Usually it fixes by the second.
+ */
+static void sbz_dsp_startup_check(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_data_check[4];
+ unsigned int cur_address = 0x390;
+ unsigned int i;
+ unsigned int failure = 0;
+ unsigned int reload = 3;
+
+ if (spec->startup_check_entered)
+ return;
+
+ spec->startup_check_entered = true;
+
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+
+ codec_dbg(codec, "Startup Check: %d ", failure);
+ if (failure)
+ codec_info(codec, "DSP not initialized properly. Attempting to fix.");
+ /*
+ * While the failure condition is true, and we haven't reached our
+ * three reload limit, continue trying to reload the driver and
+ * fix the issue.
+ */
+ while (failure && (reload != 0)) {
+ codec_info(codec, "Reloading... Tries left: %d", reload);
+ sbz_exit_chip(codec);
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ codec->patch_ops.init(codec);
+ failure = 0;
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+ reload--;
+ }
+
+ if (!failure && reload < 3)
+ codec_info(codec, "DSP fixed.");
+
+ if (!failure)
+ return;
+
+ codec_info(codec, "DSP failed to initialize properly. Either try a full shutdown or a suspend to clear the internal memory.");
+}
+
+/*
+ * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add
+ * extra precision for decibel values. If you had the dB value in floating point
+ * you would take the value after the decimal point, multiply by 64, and divide
+ * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to
+ * implement fixed point or floating point dB volumes. For now, I'll set them
+ * to 0 just incase a value has lingered from a boot into Windows.
+ */
+static void ca0132_alt_vol_setup(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void sbz_pre_dsp_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ writel(0x00820680, spec->mem_base + 0x01C);
+ writel(0x00820680, spec->mem_base + 0x01C);
+
+ snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc);
+ snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd);
+ snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe);
+ snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff);
+
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void r3di_pre_dsp_setup(struct hda_codec *codec)
+{
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, 0x40);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04);
+}
+
+
+/*
+ * These are sent before the DSP is downloaded. Not sure
+ * what they do, or if they're necessary. Could possibly
+ * be removed. Figure they're better to leave in.
+ */
+static void sbz_region2_startup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ writel(0x00000000, spec->mem_base + 0x400);
+ writel(0x00000000, spec->mem_base + 0x408);
+ writel(0x00000000, spec->mem_base + 0x40C);
+ writel(0x00880680, spec->mem_base + 0x01C);
+ writel(0x00000083, spec->mem_base + 0xC0C);
+ writel(0x00000030, spec->mem_base + 0xC00);
+ writel(0x00000000, spec->mem_base + 0xC04);
+ writel(0x00000003, spec->mem_base + 0xC0C);
+ writel(0x00000003, spec->mem_base + 0xC0C);
+ writel(0x00000003, spec->mem_base + 0xC0C);
+ writel(0x00000003, spec->mem_base + 0xC0C);
+ writel(0x000000C1, spec->mem_base + 0xC08);
+ writel(0x000000F1, spec->mem_base + 0xC08);
+ writel(0x00000001, spec->mem_base + 0xC08);
+ writel(0x000000C7, spec->mem_base + 0xC08);
+ writel(0x000000C1, spec->mem_base + 0xC08);
+ writel(0x00000080, spec->mem_base + 0xC04);
+}
+
+/*
+ * Extra init functions for alternative ca0132 codecs. Done
+ * here so they don't clutter up the main ca0132_init function
+ * anymore than they have to.
+ */
+static void ca0132_alt_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_alt_vol_setup(codec);
+
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ codec_dbg(codec, "SBZ alt_init");
+ ca0132_gpio_init(codec);
+ sbz_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->sbz_init_verbs);
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "R3DI alt_init");
+ ca0132_gpio_init(codec);
+ ca0132_gpio_setup(codec);
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING);
+ r3di_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4);
+ break;
+ }
+}
+
static int ca0132_init(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
+ bool dsp_loaded;
+
+ /*
+ * If the DSP is already downloaded, and init has been entered again,
+ * there's only two reasons for it. One, the codec has awaken from a
+ * suspended state, and in that case dspload_is_loaded will return
+ * false, and the init will be ran again. The other reason it gets
+ * re entered is on startup for some reason it triggers a suspend and
+ * resume state. In this case, it will check if the DSP is downloaded,
+ * and not run the init function again. For codecs using alt_functions,
+ * it will check if the DSP is loaded properly.
+ */
+ if (spec->dsp_state == DSP_DOWNLOADED) {
+ dsp_loaded = dspload_is_loaded(codec);
+ if (!dsp_loaded) {
+ spec->dsp_reload = true;
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ } else {
+ if (spec->quirk == QUIRK_SBZ)
+ sbz_dsp_startup_check(codec);
+ return 0;
+ }
+ }
if (spec->dsp_state != DSP_DOWNLOAD_FAILED)
spec->dsp_state = DSP_DOWNLOAD_INIT;
spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
+ if (spec->quirk == QUIRK_SBZ)
+ sbz_region2_startup(codec);
+
snd_hda_power_up_pm(codec);
ca0132_init_unsol(codec);
-
ca0132_init_params(codec);
ca0132_init_flags(codec);
+
snd_hda_sequence_write(codec, spec->base_init_verbs);
+
+ if (spec->quirk != QUIRK_NONE)
+ ca0132_alt_init(codec);
+
ca0132_download_dsp(codec);
+
ca0132_refresh_widget_caps(codec);
- ca0132_setup_defaults(codec);
- ca0132_init_analog_mic2(codec);
- ca0132_init_dmic(codec);
+
+ if (spec->quirk == QUIRK_SBZ)
+ writew(0x0107, spec->mem_base + 0x320);
+
+ switch (spec->quirk) {
+ case QUIRK_R3DI:
+ r3di_setup_defaults(codec);
+ break;
+ case QUIRK_NONE:
+ case QUIRK_ALIENWARE:
+ ca0132_setup_defaults(codec);
+ ca0132_init_analog_mic2(codec);
+ ca0132_init_dmic(codec);
+ break;
+ }
for (i = 0; i < spec->num_outputs; i++)
init_output(codec, spec->out_pins[i], spec->dacs[0]);
@@ -4590,14 +7255,45 @@ static int ca0132_init(struct hda_codec *codec)
init_input(codec, cfg->dig_in_pin, spec->dig_in);
- snd_hda_sequence_write(codec, spec->chip_init_verbs);
- snd_hda_sequence_write(codec, spec->spec_init_verbs);
+ if (!spec->use_alt_functions) {
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
+ }
- ca0132_select_out(codec);
- ca0132_select_mic(codec);
+ if (spec->quirk == QUIRK_SBZ)
+ ca0132_gpio_setup(codec);
+
+ snd_hda_sequence_write(codec, spec->spec_init_verbs);
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ sbz_setup_defaults(codec);
+ ca0132_alt_select_out(codec);
+ ca0132_alt_select_in(codec);
+ break;
+ case QUIRK_R3DI:
+ ca0132_alt_select_out(codec);
+ ca0132_alt_select_in(codec);
+ break;
+ default:
+ ca0132_select_out(codec);
+ ca0132_select_mic(codec);
+ break;
+ }
snd_hda_jack_report_sync(codec);
+ /*
+ * Re set the PlayEnhancement switch on a resume event, because the
+ * controls will not be reloaded.
+ */
+ if (spec->dsp_reload) {
+ spec->dsp_reload = false;
+ ca0132_pe_switch_set(codec);
+ }
+
snd_hda_power_down_pm(codec);
return 0;
@@ -4609,19 +7305,39 @@ static void ca0132_free(struct hda_codec *codec)
cancel_delayed_work_sync(&spec->unsol_hp_work);
snd_hda_power_up(codec);
- snd_hda_sequence_write(codec, spec->base_exit_verbs);
- ca0132_exit_chip(codec);
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ sbz_exit_chip(codec);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_shutdown(codec);
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_exit_chip(codec);
+ break;
+ default:
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_exit_chip(codec);
+ break;
+ }
snd_hda_power_down(codec);
+ if (spec->mem_base)
+ iounmap(spec->mem_base);
kfree(spec->spec_init_verbs);
kfree(codec->spec);
}
+static void ca0132_reboot_notify(struct hda_codec *codec)
+{
+ codec->patch_ops.free(codec);
+}
+
static const struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
.free = ca0132_free,
.unsol_event = snd_hda_jack_unsol_event,
+ .reboot_notify = ca0132_reboot_notify,
};
static void ca0132_config(struct hda_codec *codec)
@@ -4635,9 +7351,14 @@ static void ca0132_config(struct hda_codec *codec)
spec->multiout.dac_nids = spec->dacs;
spec->multiout.num_dacs = 3;
- spec->multiout.max_channels = 2;
- if (spec->quirk == QUIRK_ALIENWARE) {
+ if (!spec->use_alt_functions)
+ spec->multiout.max_channels = 2;
+ else
+ spec->multiout.max_channels = 6;
+
+ switch (spec->quirk) {
+ case QUIRK_ALIENWARE:
codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
snd_hda_apply_pincfgs(codec, alienware_pincfgs);
@@ -4657,7 +7378,71 @@ static void ca0132_config(struct hda_codec *codec)
spec->input_pins[2] = 0x13;
spec->shared_mic_nid = 0x7;
spec->unsol_tag_amic1 = 0x11;
- } else {
+ break;
+ case QUIRK_SBZ:
+ codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, sbz_pincfgs);
+
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, r3di_pincfgs);
+
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x07; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0x0a; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ break;
+ default:
spec->num_outputs = 2;
spec->out_pins[0] = 0x0b; /* speaker out */
spec->out_pins[1] = 0x10; /* headphone out */
@@ -4684,6 +7469,7 @@ static void ca0132_config(struct hda_codec *codec)
spec->dig_in = 0x09;
cfg->dig_in_pin = 0x0e;
cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
}
}
@@ -4694,6 +7480,8 @@ static int ca0132_prepare_verbs(struct hda_codec *codec)
struct ca0132_spec *spec = codec->spec;
spec->chip_init_verbs = ca0132_init_verbs0;
+ if (spec->quirk == QUIRK_SBZ)
+ spec->sbz_init_verbs = sbz_init_verbs;
spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL);
if (!spec->spec_init_verbs)
return -ENOMEM;
@@ -4757,9 +7545,46 @@ static int patch_ca0132(struct hda_codec *codec)
else
spec->quirk = QUIRK_NONE;
+ /* Setup BAR Region 2 for Sound Blaster Z */
+ if (spec->quirk == QUIRK_SBZ) {
+ spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20);
+ if (spec->mem_base == NULL) {
+ codec_warn(codec, "pci_iomap failed!");
+ codec_info(codec, "perhaps this is not an SBZ?");
+ spec->quirk = QUIRK_NONE;
+ }
+ }
+
spec->dsp_state = DSP_DOWNLOAD_INIT;
spec->num_mixers = 1;
- spec->mixers[0] = ca0132_mixer;
+
+ /* Set which mixers each quirk uses. */
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ spec->mixers[0] = sbz_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster Z");
+ break;
+ case QUIRK_R3DI:
+ spec->mixers[0] = r3di_mixer;
+ snd_hda_codec_set_name(codec, "Recon3Di");
+ break;
+ default:
+ spec->mixers[0] = ca0132_mixer;
+ break;
+ }
+
+ /* Setup whether or not to use alt functions/controls */
+ switch (spec->quirk) {
+ case QUIRK_SBZ:
+ case QUIRK_R3DI:
+ spec->use_alt_controls = true;
+ spec->use_alt_functions = true;
+ break;
+ default:
+ spec->use_alt_controls = false;
+ spec->use_alt_functions = false;
+ break;
+ }
spec->base_init_verbs = ca0132_base_init_verbs;
spec->base_exit_verbs = ca0132_base_exit_verbs;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 5b4dbcec6de8..dbf9910c5269 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -588,6 +588,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctl;
int i;
if (action != HDA_FIXUP_ACT_PROBE)
@@ -606,9 +607,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
/* override mic boost control */
- for (i = 0; i < spec->gen.kctls.used; i++) {
- struct snd_kcontrol_new *kctl =
- snd_array_elem(&spec->gen.kctls, i);
+ snd_array_for_each(&spec->gen.kctls, i, kctl) {
if (!strcmp(kctl->name, "Mic Boost Volume")) {
kctl->put = olpc_xo_mic_boost_put;
break;
@@ -965,6 +964,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
@@ -998,6 +998,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{ .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
{ .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
+ { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 7d7eb1354eee..8840daf9c6a3 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -510,7 +510,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
snd_info_set_text_ops(entry, per_pin, print_eld_info);
entry->c.text.write = write_eld_info;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
per_pin->proc_entry = entry;
return 0;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 01a6643fc7d4..d64dcb9a4c99 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2830,6 +2830,7 @@ static int find_ext_mic_pin(struct hda_codec *codec);
static void alc286_shutup(struct hda_codec *codec)
{
+ const struct hda_pincfg *pin;
int i;
int mic_pin = find_ext_mic_pin(codec);
/* don't shut up pins when unloading the driver; otherwise it breaks
@@ -2837,8 +2838,7 @@ static void alc286_shutup(struct hda_codec *codec)
*/
if (codec->bus->shutdown)
return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ snd_array_for_each(&codec->init_pins, i, pin) {
/* use read here for syncing after issuing each verb */
if (pin->nid != mic_pin)
snd_hda_codec_read(codec, pin->nid, 0,
@@ -3653,30 +3653,37 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
}
}
-static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action, hda_nid_t pin)
{
struct alc_spec *spec = codec->spec;
+
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mute_led_polarity = 0;
- spec->mute_led_nid = 0x18;
+ spec->mute_led_nid = pin;
spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
spec->gen.vmaster_mute_enum = 1;
codec->power_filter = led_power_filter;
}
}
+static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
+}
+
static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
- struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->mute_led_polarity = 0;
- spec->mute_led_nid = 0x19;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
- codec->power_filter = led_power_filter;
- }
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
+}
+
+static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
}
/* update LED status via GPIO */
@@ -5387,6 +5394,9 @@ static void alc274_fixup_bind_dacs(struct hda_codec *codec,
/* for dell wmi mic mute led */
#include "dell_wmi_helper.c"
+/* for alc295_fixup_hp_top_speakers */
+#include "hp_x360_helper.c"
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -5413,6 +5423,7 @@ enum {
ALC269_FIXUP_HP_MUTE_LED,
ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_HP_MUTE_LED_MIC3,
ALC269_FIXUP_HP_GPIO_LED,
ALC269_FIXUP_HP_GPIO_MIC1_LED,
ALC269_FIXUP_HP_LINE1_MIC1_LED,
@@ -5506,6 +5517,7 @@ enum {
ALC298_FIXUP_TPT470_DOCK,
ALC255_FIXUP_DUMMY_LINEOUT_VERB,
ALC255_FIXUP_DELL_HEADSET_MIC,
+ ALC295_FIXUP_HP_X360,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5672,6 +5684,10 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_hp_mute_led_mic2,
},
+ [ALC269_FIXUP_HP_MUTE_LED_MIC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic3,
+ },
[ALC269_FIXUP_HP_GPIO_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc269_fixup_hp_gpio_led,
@@ -6375,6 +6391,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
+ [ALC295_FIXUP_HP_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_hp_top_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3
+ }
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -6494,6 +6516,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
+ SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
@@ -6580,7 +6603,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x3138, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
- SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
@@ -6752,6 +6774,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x1b, 0x01111010},
{0x1e, 0x01451130},
{0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60140},
{0x14, 0x90170110},
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 5101f40f6fbd..93b8cfc6636f 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -662,7 +662,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
struct snd_info_entry *entry;
if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = wm_proc_regs_write;
}
}
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 8dabd4d0211d..d7366ade5a25 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -926,7 +926,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
struct snd_info_entry *entry;
if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = wm_proc_regs_write;
}
}
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
index c241dc06dd92..904e3c4f4dfe 100644
--- a/sound/pci/lola/lola_proc.c
+++ b/sound/pci/lola/lola_proc.c
@@ -214,7 +214,7 @@ void lola_proc_debug_new(struct lola *chip)
snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = lola_proc_codec_rw_write;
}
if (!snd_card_proc_new(chip->card, "regs", &entry))
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 4ca12665ff73..81af21ac1439 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -1052,10 +1052,10 @@ static int add_controls(struct oxygen *chip,
[CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch",
[CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch",
};
- unsigned int i, j;
+ unsigned int i;
struct snd_kcontrol_new template;
struct snd_kcontrol *ctl;
- int err;
+ int j, err;
for (i = 0; i < count; ++i) {
template = controls[i];
@@ -1086,11 +1086,11 @@ static int add_controls(struct oxygen *chip,
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
- for (j = 0; j < CONTROL_COUNT; ++j)
- if (!strcmp(ctl->id.name, known_ctl_names[j])) {
- chip->controls[j] = ctl;
- ctl->private_free = oxygen_any_ctl_free;
- }
+ j = match_string(known_ctl_names, CONTROL_COUNT, ctl->id.name);
+ if (j >= 0) {
+ chip->controls[j] = ctl;
+ ctl->private_free = oxygen_any_ctl_free;
+ }
}
return 0;
}
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index f9ae72f28ddc..e57da4036231 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1465,7 +1465,7 @@ static void pcxhr_proc_init(struct snd_pcxhr *chip)
!snd_card_proc_new(chip->card, "gpio", &entry)) {
snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
entry->c.text.write = pcxhr_proc_gpo_write;
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
}
if (!snd_card_proc_new(chip->card, "ltc", &entry))
snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc);