4318 lines
142 KiB
C
4318 lines
142 KiB
C
/* $NetBSD: azalia_codec.c,v 1.77 2009/04/07 14:47:53 stacktic Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2005 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by TAMURA Kent
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.77 2009/04/07 14:47:53 stacktic Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/null.h>
|
|
#include <sys/systm.h>
|
|
#include <dev/pci/azalia.h>
|
|
|
|
#ifdef MAX_VOLUME_255
|
|
# define MIXER_DELTA(n) (AUDIO_MAX_GAIN / (n))
|
|
#else
|
|
# define MIXER_DELTA(n) (1)
|
|
#endif
|
|
#define AZ_CLASS_INPUT 0
|
|
#define AZ_CLASS_OUTPUT 1
|
|
#define AZ_CLASS_RECORD 2
|
|
#define AZ_CLASS_PLAYBACK 3
|
|
#define AZ_CLASS_MIXER 4
|
|
#define AzaliaCplayback "playback"
|
|
#define AzaliaCmixer "mix"
|
|
#define AzaliaNfront "front"
|
|
#define AzaliaNclfe "clfe"
|
|
#define AzaliaNside "side"
|
|
#define AzaliaNdigital "spdif"
|
|
#define ENUM_OFFON .un.e={2, {{{AudioNoff, 0}, 0}, {{AudioNon, 0}, 1}}}
|
|
#define ENUM_IO .un.e={2, {{{AudioNinput, 0}, 0}, {{AudioNoutput, 0}, 1}}}
|
|
#define ENUM_V .un.v={{ "", 0 }, 0, 0 }
|
|
#define AZ_MIXER_CLASSES \
|
|
{{AZ_CLASS_INPUT, {AudioCinputs, 0}, AUDIO_MIXER_CLASS, \
|
|
AZ_CLASS_INPUT, 0, 0, ENUM_V }, 0, 0, }, \
|
|
{{AZ_CLASS_OUTPUT, {AudioCoutputs, 0}, AUDIO_MIXER_CLASS, \
|
|
AZ_CLASS_OUTPUT, 0, 0, ENUM_V }, 0, 0, }, \
|
|
{{AZ_CLASS_RECORD, {AudioCrecord, 0}, AUDIO_MIXER_CLASS, \
|
|
AZ_CLASS_RECORD, 0, 0, ENUM_V }, 0, 0, }, \
|
|
{{AZ_CLASS_PLAYBACK, {AzaliaCplayback, 0}, AUDIO_MIXER_CLASS, \
|
|
AZ_CLASS_PLAYBACK, 0, 0, ENUM_V }, 0, 0, }, \
|
|
{{AZ_CLASS_MIXER, {AzaliaCmixer, 0}, AUDIO_MIXER_CLASS, \
|
|
AZ_CLASS_MIXER, 0, 0, ENUM_V }, 0, 0, }
|
|
#define AZ_MIXER_SPDIF(cl, nid) \
|
|
{{0, {AzaliaNdigital, 0}, AUDIO_MIXER_SET, cl, 0, 0, \
|
|
.un.s={6, {{{"v", 0}, CORB_DCC_V}, {{"vcfg", 0}, CORB_DCC_VCFG}, \
|
|
{{"pre", 0}, CORB_DCC_PRE}, {{"copy", 0}, CORB_DCC_COPY}, \
|
|
{{"pro", 0}, CORB_DCC_PRO}, {{"l", 0}, CORB_DCC_L}}}}, \
|
|
nid, MI_TARGET_SPDIF}, \
|
|
{{0, {AzaliaNdigital".cc", 0}, AUDIO_MIXER_VALUE, cl, 0, 0, \
|
|
.un.v={{"", 0}, 1, 1}}, nid, MI_TARGET_SPDIF_CC}
|
|
|
|
|
|
static int generic_codec_init_dacgroup(codec_t *);
|
|
static int generic_codec_add_dacgroup(codec_t *, int, uint32_t);
|
|
static int generic_codec_find_pin(const codec_t *, int, int, uint32_t);
|
|
static int generic_codec_find_dac(const codec_t *, int, int);
|
|
|
|
static int generic_mixer_init(codec_t *);
|
|
static int generic_mixer_autoinit(codec_t *);
|
|
static int generic_mixer_init_widget(const codec_t *, widget_t *, nid_t);
|
|
|
|
static int generic_mixer_fix_indexes(codec_t *);
|
|
static int generic_mixer_default(codec_t *);
|
|
static int generic_mixer_pin_sense(codec_t *);
|
|
static int generic_mixer_widget_name(const codec_t *, widget_t *);
|
|
static int generic_mixer_create_virtual(codec_t *);
|
|
static int generic_mixer_delete(codec_t *);
|
|
static void generic_mixer_cat_names
|
|
(char *, size_t, const char *, const char *, const char *);
|
|
static int generic_mixer_ensure_capacity(codec_t *, size_t);
|
|
static int generic_mixer_get(const codec_t *, nid_t, int, mixer_ctrl_t *);
|
|
static int generic_mixer_set(codec_t *, nid_t, int, const mixer_ctrl_t *);
|
|
static int generic_mixer_pinctrl(codec_t *, nid_t, uint32_t);
|
|
static u_char generic_mixer_from_device_value
|
|
(const codec_t *, nid_t, int, uint32_t );
|
|
static uint32_t generic_mixer_to_device_value
|
|
(const codec_t *, nid_t, int, u_char);
|
|
static uint32_t generic_mixer_max(const codec_t *, nid_t, int);
|
|
static bool generic_mixer_validate_value
|
|
(const codec_t *, nid_t, int, u_char);
|
|
static int generic_set_port(codec_t *, mixer_ctrl_t *);
|
|
static int generic_get_port(codec_t *, mixer_ctrl_t *);
|
|
|
|
static int alc260_init_dacgroup(codec_t *);
|
|
static int alc260_mixer_init(codec_t *);
|
|
static int alc260_set_port(codec_t *, mixer_ctrl_t *);
|
|
static int alc260_get_port(codec_t *, mixer_ctrl_t *);
|
|
static int alc260_unsol_event(codec_t *, int);
|
|
static int alc262_init_widget(const codec_t *, widget_t *, nid_t);
|
|
static int alc268_init_dacgroup(codec_t *);
|
|
static int alc662_init_dacgroup(codec_t *);
|
|
static int alc861_init_dacgroup(codec_t *);
|
|
static int alc861vdgr_init_dacgroup(codec_t *);
|
|
static int alc880_init_dacgroup(codec_t *);
|
|
static int alc880_mixer_init(codec_t *);
|
|
static int alc882_init_dacgroup(codec_t *);
|
|
static int alc882_mixer_init(codec_t *);
|
|
static int alc882_set_port(codec_t *, mixer_ctrl_t *);
|
|
static int alc882_get_port(codec_t *, mixer_ctrl_t *);
|
|
static int alc883_init_dacgroup(codec_t *);
|
|
static int alc883_mixer_init(codec_t *);
|
|
static int alc885_init_dacgroup(codec_t *);
|
|
static int alc888_init_dacgroup(codec_t *);
|
|
static int alc888_init_widget(const codec_t *, widget_t *, nid_t);
|
|
static int alc888_mixer_init(codec_t *);
|
|
static int ad1981hd_init_widget(const codec_t *, widget_t *, nid_t);
|
|
static int ad1981hd_mixer_init(codec_t *);
|
|
static int ad1983_mixer_init(codec_t *);
|
|
static int ad1983_unsol_event(codec_t *, int);
|
|
static int ad1984_init_dacgroup(codec_t *);
|
|
static int ad1984_init_widget(const codec_t *, widget_t *, nid_t);
|
|
static int ad1984_mixer_init(codec_t *);
|
|
static int ad1984_unsol_event(codec_t *, int);
|
|
static int ad1986a_init_dacgroup(codec_t *);
|
|
static int ad1986a_mixer_init(codec_t *);
|
|
static int ad1988_init_dacgroup(codec_t *);
|
|
static int cmi9880_init_dacgroup(codec_t *);
|
|
static int cmi9880_mixer_init(codec_t *);
|
|
static int stac9221_init_dacgroup(codec_t *);
|
|
static int stac9221_mixer_init(codec_t *);
|
|
static int stac9221_gpio_unmute(codec_t *, int);
|
|
static int stac9200_mixer_init(codec_t *);
|
|
static int stac9200_unsol_event(codec_t *, int);
|
|
static int atihdmi_init_dacgroup(codec_t *);
|
|
|
|
|
|
int
|
|
azalia_codec_init_vtbl(codec_t *this)
|
|
{
|
|
size_t extra_size;
|
|
|
|
/**
|
|
* We can refer this->vid and this->subid.
|
|
*/
|
|
DPRINTF(("%s: vid=%08x subid=%08x\n", __func__, this->vid, this->subid));
|
|
extra_size = 0;
|
|
this->name = NULL;
|
|
this->init_dacgroup = generic_codec_init_dacgroup;
|
|
this->mixer_init = generic_mixer_init;
|
|
this->mixer_delete = generic_mixer_delete;
|
|
this->set_port = generic_set_port;
|
|
this->get_port = generic_get_port;
|
|
this->unsol_event = NULL;
|
|
switch (this->vid) {
|
|
case 0x10027919:
|
|
case 0x1002793c:
|
|
this->name = "ATI RS600 HDMI";
|
|
this->init_dacgroup = atihdmi_init_dacgroup;
|
|
break;
|
|
case 0x1002791a:
|
|
this->name = "ATI RS690/780 HDMI";
|
|
this->init_dacgroup = atihdmi_init_dacgroup;
|
|
break;
|
|
case 0x1002aa01:
|
|
this->name = "ATI R600 HDMI";
|
|
this->init_dacgroup = atihdmi_init_dacgroup;
|
|
break;
|
|
case 0x10ec0260:
|
|
this->name = "Realtek ALC260";
|
|
this->mixer_init = alc260_mixer_init;
|
|
this->init_dacgroup = alc260_init_dacgroup;
|
|
this->set_port = alc260_set_port;
|
|
this->unsol_event = alc260_unsol_event;
|
|
extra_size = 1;
|
|
break;
|
|
case 0x10ec0262:
|
|
this->name = "Realtek ALC262";
|
|
this->init_widget = alc262_init_widget;
|
|
break;
|
|
case 0x10ec0268:
|
|
this->name = "Realtek ALC268";
|
|
this->init_dacgroup = alc268_init_dacgroup;
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
this->init_widget = generic_mixer_init_widget;
|
|
break;
|
|
case 0x10ec0269:
|
|
this->name = "Realtek ALC269";
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
this->init_widget = generic_mixer_init_widget;
|
|
break;
|
|
case 0x10ec0662:
|
|
this->name = "Realtek ALC662-GR";
|
|
this->init_dacgroup = alc662_init_dacgroup;
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
this->init_widget = generic_mixer_init_widget;
|
|
break;
|
|
case 0x10ec0663:
|
|
this->name = "Realtek ALC663";
|
|
this->init_dacgroup = alc662_init_dacgroup;
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
this->init_widget = generic_mixer_init_widget;
|
|
break;
|
|
case 0x10ec0861:
|
|
this->name = "Realtek ALC861";
|
|
this->init_dacgroup = alc861_init_dacgroup;
|
|
break;
|
|
case 0x10ec0862:
|
|
this->name = "Realtek ALC861-VD-GR";
|
|
this->init_dacgroup = alc861vdgr_init_dacgroup;
|
|
break;
|
|
case 0x10ec0880:
|
|
this->name = "Realtek ALC880";
|
|
this->init_dacgroup = alc880_init_dacgroup;
|
|
this->mixer_init = alc880_mixer_init;
|
|
break;
|
|
case 0x10ec0882:
|
|
this->name = "Realtek ALC882";
|
|
this->init_dacgroup = alc882_init_dacgroup;
|
|
this->mixer_init = alc882_mixer_init;
|
|
this->get_port = alc882_get_port;
|
|
this->set_port = alc882_set_port;
|
|
break;
|
|
case 0x10ec0883:
|
|
/* ftp://209.216.61.149/pc/audio/ALC883_DataSheet_1.3.pdf */
|
|
this->name = "Realtek ALC883";
|
|
this->init_dacgroup = alc883_init_dacgroup;
|
|
this->mixer_init = alc883_mixer_init;
|
|
this->get_port = alc882_get_port;
|
|
this->set_port = alc882_set_port;
|
|
break;
|
|
case 0x10ec0885:
|
|
this->name = "Realtek ALC885";
|
|
this->init_dacgroup = alc885_init_dacgroup;
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
this->init_widget = generic_mixer_init_widget;
|
|
break;
|
|
case 0x10ec0888:
|
|
this->name = "Realtek ALC888";
|
|
this->init_dacgroup = alc888_init_dacgroup;
|
|
this->init_widget = alc888_init_widget;
|
|
this->mixer_init = alc888_mixer_init;
|
|
break;
|
|
case 0x11d41981:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1981HD,00.html */
|
|
this->name = "Analog Devices AD1981HD";
|
|
this->init_widget = ad1981hd_init_widget;
|
|
this->mixer_init = ad1981hd_mixer_init;
|
|
break;
|
|
case 0x11d41983:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1983,00.html */
|
|
this->name = "Analog Devices AD1983";
|
|
this->mixer_init = ad1983_mixer_init;
|
|
this->unsol_event = ad1983_unsol_event;
|
|
break;
|
|
case 0x11d41984:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1984,00.html */
|
|
this->name = "Analog Devices AD1984";
|
|
this->init_dacgroup = ad1984_init_dacgroup;
|
|
this->init_widget = ad1984_init_widget;
|
|
this->mixer_init = ad1984_mixer_init;
|
|
this->unsol_event = ad1984_unsol_event;
|
|
break;
|
|
case 0x11d4194a:
|
|
/* http://www.analog.com/static/imported-files/data_sheets/AD1984A.pdf */
|
|
this->name = "Analog Devices AD1984A";
|
|
this->init_dacgroup = ad1984_init_dacgroup;
|
|
this->init_widget = ad1984_init_widget;
|
|
this->mixer_init = ad1984_mixer_init;
|
|
this->unsol_event = ad1984_unsol_event;
|
|
break;
|
|
case 0x11d41986:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1986A,00.html */
|
|
this->name = "Analog Devices AD1986A";
|
|
this->init_dacgroup = ad1986a_init_dacgroup;
|
|
this->mixer_init = ad1986a_mixer_init;
|
|
break;
|
|
case 0x11d41988:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1988A,00.html */
|
|
this->name = "Analog Devices AD1988A";
|
|
this->init_dacgroup = ad1988_init_dacgroup;
|
|
break;
|
|
case 0x11d4198b:
|
|
/* http://www.analog.com/en/prod/0,2877,AD1988B,00.html */
|
|
this->name = "Analog Devices AD1988B";
|
|
this->init_dacgroup = ad1988_init_dacgroup;
|
|
break;
|
|
case 0x434d4980:
|
|
this->name = "CMedia CMI9880";
|
|
this->init_dacgroup = cmi9880_init_dacgroup;
|
|
this->mixer_init = cmi9880_mixer_init;
|
|
break;
|
|
case 0x83847612:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9230X";
|
|
break;
|
|
case 0x83847613:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9230D";
|
|
break;
|
|
case 0x83847614:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9229X";
|
|
break;
|
|
case 0x83847615:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9229D";
|
|
break;
|
|
case 0x83847616:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9228X";
|
|
break;
|
|
case 0x83847617:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9228D";
|
|
break;
|
|
case 0x83847618:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9227X";
|
|
break;
|
|
case 0x83847619:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17122893 */
|
|
this->name = "Sigmatel STAC9227D";
|
|
break;
|
|
case 0x83847680:
|
|
this->name = "Sigmatel STAC9221";
|
|
this->init_dacgroup = stac9221_init_dacgroup;
|
|
this->mixer_init = stac9221_mixer_init;
|
|
break;
|
|
case 0x83847683:
|
|
this->name = "Sigmatel STAC9221D";
|
|
this->init_dacgroup = stac9221_init_dacgroup;
|
|
break;
|
|
case 0x83847690:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17812077 */
|
|
this->name = "Sigmatel STAC9200";
|
|
this->mixer_init = stac9200_mixer_init;
|
|
this->unsol_event = stac9200_unsol_event;
|
|
break;
|
|
case 0x83847691:
|
|
/* http://www.idt.com/products/getDoc.cfm?docID=17812077 */
|
|
this->name = "Sigmatel STAC9200D";
|
|
this->mixer_init = stac9200_mixer_init;
|
|
this->unsol_event = stac9200_unsol_event;
|
|
break;
|
|
}
|
|
if (extra_size > 0) {
|
|
this->extra = malloc(sizeof(uint32_t) * extra_size,
|
|
M_DEVBUF, M_ZERO | M_NOWAIT);
|
|
if (this->extra == NULL) {
|
|
aprint_error_dev(this->dev, "Not enough memory\n");
|
|
return ENOMEM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* functions for generic codecs
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
generic_codec_init_dacgroup(codec_t *this)
|
|
{
|
|
int i, j, assoc, group;
|
|
|
|
/*
|
|
* grouping DACs
|
|
* [0] the lowest assoc DACs
|
|
* [1] the lowest assoc digital outputs
|
|
* [2] the 2nd assoc DACs
|
|
* :
|
|
*/
|
|
this->dacs.ngroups = 0;
|
|
for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) {
|
|
generic_codec_add_dacgroup(this, assoc, 0);
|
|
generic_codec_add_dacgroup(this, assoc, COP_AWCAP_DIGITAL);
|
|
}
|
|
|
|
/* find DACs which do not connect with any pins by default */
|
|
DPRINTF(("%s: find non-connected DACs\n", __func__));
|
|
FOR_EACH_WIDGET(this, i) {
|
|
bool found;
|
|
|
|
if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT)
|
|
continue;
|
|
found = FALSE;
|
|
for (group = 0; group < this->dacs.ngroups; group++) {
|
|
for (j = 0; j < this->dacs.groups[group].nconv; j++) {
|
|
if (i == this->dacs.groups[group].conv[j]) {
|
|
found = TRUE;
|
|
group = this->dacs.ngroups;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
continue;
|
|
if (this->dacs.ngroups >= 32)
|
|
break;
|
|
this->dacs.groups[this->dacs.ngroups].nconv = 1;
|
|
this->dacs.groups[this->dacs.ngroups].conv[0] = i;
|
|
this->dacs.ngroups++;
|
|
}
|
|
this->dacs.cur = 0;
|
|
|
|
/* enumerate ADCs */
|
|
this->adcs.ngroups = 0;
|
|
FOR_EACH_WIDGET(this, i) {
|
|
if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT)
|
|
continue;
|
|
this->adcs.groups[this->adcs.ngroups].nconv = 1;
|
|
this->adcs.groups[this->adcs.ngroups].conv[0] = i;
|
|
this->adcs.ngroups++;
|
|
if (this->adcs.ngroups >= 32)
|
|
break;
|
|
}
|
|
this->adcs.cur = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_codec_add_dacgroup(codec_t *this, int assoc, uint32_t digital)
|
|
{
|
|
int i, j, n, dac, seq;
|
|
|
|
n = 0;
|
|
for (seq = 0 ; seq < CORB_CD_SEQUENCE_MAX; seq++) {
|
|
i = generic_codec_find_pin(this, assoc, seq, digital);
|
|
if (i < 0)
|
|
continue;
|
|
dac = generic_codec_find_dac(this, i, 0);
|
|
if (dac < 0)
|
|
continue;
|
|
/* duplication check */
|
|
for (j = 0; j < n; j++) {
|
|
if (this->dacs.groups[this->dacs.ngroups].conv[j] == dac)
|
|
break;
|
|
}
|
|
if (j < n) /* this group already has <dac> */
|
|
continue;
|
|
this->dacs.groups[this->dacs.ngroups].conv[n++] = dac;
|
|
DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n",
|
|
__func__, assoc, seq, this->dacs.ngroups, n-1));
|
|
}
|
|
if (n <= 0) /* no such DACs */
|
|
return 0;
|
|
this->dacs.groups[this->dacs.ngroups].nconv = n;
|
|
|
|
/* check if the same combination is already registered */
|
|
for (i = 0; i < this->dacs.ngroups; i++) {
|
|
if (n != this->dacs.groups[i].nconv)
|
|
continue;
|
|
for (j = 0; j < n; j++) {
|
|
if (this->dacs.groups[this->dacs.ngroups].conv[j] !=
|
|
this->dacs.groups[i].conv[j])
|
|
break;
|
|
}
|
|
if (j >= n) /* matched */
|
|
return 0;
|
|
}
|
|
/* found no equivalent group */
|
|
this->dacs.ngroups++;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_codec_find_pin(const codec_t *this, int assoc, int seq, uint32_t digital)
|
|
{
|
|
int i;
|
|
|
|
FOR_EACH_WIDGET(this, i) {
|
|
if (this->w[i].type != COP_AWTYPE_PIN_COMPLEX)
|
|
continue;
|
|
if ((this->w[i].d.pin.cap & COP_PINCAP_OUTPUT) == 0)
|
|
continue;
|
|
if ((this->w[i].widgetcap & COP_AWCAP_DIGITAL) != digital)
|
|
continue;
|
|
if (this->w[i].d.pin.association != assoc)
|
|
continue;
|
|
if (this->w[i].d.pin.sequence == seq) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
generic_codec_find_dac(const codec_t *this, int index, int depth)
|
|
{
|
|
const widget_t *w;
|
|
int i, j, ret;
|
|
|
|
w = &this->w[index];
|
|
if (w->type == COP_AWTYPE_AUDIO_OUTPUT) {
|
|
DPRINTF(("%s: DAC: nid=0x%x index=%d\n",
|
|
__func__, w->nid, index));
|
|
return index;
|
|
}
|
|
if (++depth > 50) {
|
|
return -1;
|
|
}
|
|
if (w->selected >= 0) {
|
|
j = w->connections[w->selected];
|
|
if (VALID_WIDGET_NID(j, this)) {
|
|
ret = generic_codec_find_dac(this, j, depth);
|
|
if (ret >= 0) {
|
|
DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
|
|
__func__, w->nid, index));
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < w->nconnections; i++) {
|
|
j = w->connections[i];
|
|
if (!VALID_WIDGET_NID(j, this))
|
|
continue;
|
|
ret = generic_codec_find_dac(this, j, depth);
|
|
if (ret >= 0) {
|
|
DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
|
|
__func__, w->nid, index));
|
|
return ret;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Generic mixer functions
|
|
* ---------------------------------------------------------------- */
|
|
|
|
#define GMIDPRINTF(x) do {} while (0/*CONSTCOND*/)
|
|
|
|
static int
|
|
generic_mixer_init(codec_t *this)
|
|
{
|
|
/*
|
|
* pin "<color>%2.2x"
|
|
* audio output "dac%2.2x"
|
|
* audio input "adc%2.2x"
|
|
* mixer "mixer%2.2x"
|
|
* selector "sel%2.2x"
|
|
*/
|
|
mixer_item_t *m;
|
|
int err, i, j, k;
|
|
|
|
this->maxmixers = 10;
|
|
this->nmixers = 0;
|
|
this->mixers = malloc(sizeof(mixer_item_t) * this->maxmixers,
|
|
M_DEVBUF, M_ZERO | M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
|
|
/* register classes */
|
|
DPRINTF(("%s: register classes\n", __func__));
|
|
m = &this->mixers[AZ_CLASS_INPUT];
|
|
m->devinfo.index = AZ_CLASS_INPUT;
|
|
strlcpy(m->devinfo.label.name, AudioCinputs,
|
|
sizeof(m->devinfo.label.name));
|
|
m->devinfo.type = AUDIO_MIXER_CLASS;
|
|
m->devinfo.mixer_class = AZ_CLASS_INPUT;
|
|
m->devinfo.next = AUDIO_MIXER_LAST;
|
|
m->devinfo.prev = AUDIO_MIXER_LAST;
|
|
m->nid = 0;
|
|
|
|
m = &this->mixers[AZ_CLASS_OUTPUT];
|
|
m->devinfo.index = AZ_CLASS_OUTPUT;
|
|
strlcpy(m->devinfo.label.name, AudioCoutputs,
|
|
sizeof(m->devinfo.label.name));
|
|
m->devinfo.type = AUDIO_MIXER_CLASS;
|
|
m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
|
|
m->devinfo.next = AUDIO_MIXER_LAST;
|
|
m->devinfo.prev = AUDIO_MIXER_LAST;
|
|
m->nid = 0;
|
|
|
|
m = &this->mixers[AZ_CLASS_RECORD];
|
|
m->devinfo.index = AZ_CLASS_RECORD;
|
|
strlcpy(m->devinfo.label.name, AudioCrecord,
|
|
sizeof(m->devinfo.label.name));
|
|
m->devinfo.type = AUDIO_MIXER_CLASS;
|
|
m->devinfo.mixer_class = AZ_CLASS_RECORD;
|
|
m->devinfo.next = AUDIO_MIXER_LAST;
|
|
m->devinfo.prev = AUDIO_MIXER_LAST;
|
|
m->nid = 0;
|
|
|
|
m = &this->mixers[AZ_CLASS_PLAYBACK];
|
|
m->devinfo.index = AZ_CLASS_PLAYBACK;
|
|
strlcpy(m->devinfo.label.name, AzaliaCplayback,
|
|
sizeof(m->devinfo.label.name));
|
|
m->devinfo.type = AUDIO_MIXER_CLASS;
|
|
m->devinfo.mixer_class = AZ_CLASS_PLAYBACK;
|
|
m->devinfo.next = AUDIO_MIXER_LAST;
|
|
m->devinfo.prev = AUDIO_MIXER_LAST;
|
|
m->nid = 0;
|
|
|
|
m = &this->mixers[AZ_CLASS_MIXER];
|
|
m->devinfo.index = AZ_CLASS_MIXER;
|
|
strlcpy(m->devinfo.label.name, AzaliaCmixer,
|
|
sizeof(m->devinfo.label.name));
|
|
m->devinfo.type = AUDIO_MIXER_CLASS;
|
|
m->devinfo.mixer_class = AZ_CLASS_MIXER;
|
|
m->devinfo.next = AUDIO_MIXER_LAST;
|
|
m->devinfo.prev = AUDIO_MIXER_LAST;
|
|
m->nid = 0;
|
|
|
|
this->nmixers = AZ_CLASS_MIXER + 1;
|
|
|
|
#define MIXER_REG_PROLOG \
|
|
mixer_devinfo_t *d; \
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1); \
|
|
if (err) \
|
|
return err; \
|
|
m = &this->mixers[this->nmixers]; \
|
|
d = &m->devinfo; \
|
|
m->nid = i
|
|
|
|
FOR_EACH_WIDGET(this, i) {
|
|
const widget_t *w;
|
|
|
|
w = &this->w[i];
|
|
|
|
/* skip unconnected pins */
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX) {
|
|
uint8_t conn =
|
|
(w->d.pin.config & CORB_CD_PORT_MASK) >> 30;
|
|
if (conn == 1) /* no physical connection */
|
|
continue;
|
|
}
|
|
|
|
/* selector */
|
|
if (w->type != COP_AWTYPE_AUDIO_MIXER &&
|
|
w->type != COP_AWTYPE_POWER && w->nconnections >= 2) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: selector %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.source", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
else
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
m->target = MI_TARGET_CONNLIST;
|
|
for (j = 0, k = 0; j < w->nconnections && k < 32; j++) {
|
|
uint8_t conn;
|
|
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
continue;
|
|
/* skip unconnected pins */
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
conn);
|
|
if (conn == 1)
|
|
continue;
|
|
GMIDPRINTF(("%s: selector %d=%s\n", __func__, j,
|
|
this->w[w->connections[j]].name));
|
|
d->un.e.member[k].ord = j;
|
|
strlcpy(d->un.e.member[k].label.name,
|
|
this->w[w->connections[j]].name,
|
|
MAX_AUDIO_DEV_LEN);
|
|
k++;
|
|
}
|
|
d->un.e.num_mem = k;
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* output mute */
|
|
if (w->widgetcap & COP_AWCAP_OUTAMP &&
|
|
w->outamp_cap & COP_AMPCAP_MUTE) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: output mute %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.mute", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
|
else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = MI_TARGET_OUTAMP;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNoff,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNon,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* output gain */
|
|
if (w->widgetcap & COP_AWCAP_OUTAMP
|
|
&& COP_AMPCAP_NUMSTEPS(w->outamp_cap)) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: output gain %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s", w->name);
|
|
d->type = AUDIO_MIXER_VALUE;
|
|
if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
|
else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = MI_TARGET_OUTAMP;
|
|
d->un.v.num_channels = WIDGET_CHANNELS(w);
|
|
#ifdef MAX_VOLUME_255
|
|
d->un.v.units.name[0] = 0;
|
|
#else
|
|
snprintf(d->un.v.units.name, sizeof(d->un.v.units.name),
|
|
"0.25x%ddB", COP_AMPCAP_STEPSIZE(w->outamp_cap)+1);
|
|
#endif
|
|
d->un.v.delta =
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* input mute */
|
|
if (w->widgetcap & COP_AWCAP_INAMP &&
|
|
w->inamp_cap & COP_AMPCAP_MUTE) {
|
|
GMIDPRINTF(("%s: input mute %s\n", __func__, w->name));
|
|
if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
|
|
w->type != COP_AWTYPE_AUDIO_MIXER) {
|
|
MIXER_REG_PROLOG;
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.mute", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = 0;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name,
|
|
AudioNoff, MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name,
|
|
AudioNon, MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
} else {
|
|
uint8_t conn;
|
|
|
|
for (j = 0; j < w->nconnections; j++) {
|
|
MIXER_REG_PROLOG;
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
continue;
|
|
|
|
/* skip unconnected pins */
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
conn);
|
|
if (conn == 1)
|
|
continue;
|
|
|
|
GMIDPRINTF(("%s: input mute %s.%s\n", __func__,
|
|
w->name, this->w[w->connections[j]].name));
|
|
generic_mixer_cat_names(
|
|
d->label.name,
|
|
sizeof(d->label.name),
|
|
w->name, this->w[w->connections[j]].name,
|
|
"mute");
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = j;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name,
|
|
AudioNoff, MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name,
|
|
AudioNon, MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* input gain */
|
|
if (w->widgetcap & COP_AWCAP_INAMP
|
|
&& COP_AMPCAP_NUMSTEPS(w->inamp_cap)) {
|
|
GMIDPRINTF(("%s: input gain %s\n", __func__, w->name));
|
|
if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
|
|
w->type != COP_AWTYPE_AUDIO_MIXER) {
|
|
MIXER_REG_PROLOG;
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s", w->name);
|
|
d->type = AUDIO_MIXER_VALUE;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = 0;
|
|
d->un.v.num_channels = WIDGET_CHANNELS(w);
|
|
#ifdef MAX_VOLUME_255
|
|
d->un.v.units.name[0] = 0;
|
|
#else
|
|
snprintf(d->un.v.units.name,
|
|
sizeof(d->un.v.units.name), "0.25x%ddB",
|
|
COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
|
|
#endif
|
|
d->un.v.delta =
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
|
|
this->nmixers++;
|
|
} else {
|
|
uint8_t conn;
|
|
|
|
for (j = 0; j < w->nconnections; j++) {
|
|
MIXER_REG_PROLOG;
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
continue;
|
|
/* skip unconnected pins */
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
conn);
|
|
if (conn == 1)
|
|
continue;
|
|
GMIDPRINTF(("%s: input gain %s.%s\n", __func__,
|
|
w->name, this->w[w->connections[j]].name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.%s", w->name,
|
|
this->w[w->connections[j]].name);
|
|
d->type = AUDIO_MIXER_VALUE;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = j;
|
|
d->un.v.num_channels = WIDGET_CHANNELS(w);
|
|
#ifdef MAX_VOLUME_255
|
|
d->un.v.units.name[0] = 0;
|
|
#else
|
|
snprintf(d->un.v.units.name,
|
|
sizeof(d->un.v.units.name), "0.25x%ddB",
|
|
COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
|
|
#endif
|
|
d->un.v.delta =
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
|
|
this->nmixers++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* pin direction */
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
|
|
w->d.pin.cap & COP_PINCAP_OUTPUT &&
|
|
w->d.pin.cap & COP_PINCAP_INPUT) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: pin dir %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.dir", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
m->target = MI_TARGET_PINDIR;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNinput,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNoutput,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* pin headphone-boost */
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
|
|
w->d.pin.cap & COP_PINCAP_HEADPHONE) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: hpboost %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.boost", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
m->target = MI_TARGET_PINBOOST;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNoff,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNon,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
|
|
w->d.pin.cap & COP_PINCAP_EAPD) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: eapd %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.eapd", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
m->target = MI_TARGET_EAPD;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNoff,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNon,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
|
|
w->d.pin.cap & COP_PINCAP_BALANCE) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: balance %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.balance", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = MI_TARGET_BALANCE;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNoff,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNon,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
if (w->widgetcap & COP_AWCAP_LRSWAP) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: lrswap %s\n", __func__, w->name));
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
"%s.lrswap", w->name);
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX)
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
else if (w->type == COP_AWTYPE_AUDIO_INPUT)
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
else
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
m->target = MI_TARGET_LRSWAP;
|
|
d->un.e.num_mem = 2;
|
|
d->un.e.member[0].ord = 0;
|
|
strlcpy(d->un.e.member[0].label.name, AudioNoff,
|
|
MAX_AUDIO_DEV_LEN);
|
|
d->un.e.member[1].ord = 1;
|
|
strlcpy(d->un.e.member[1].label.name, AudioNon,
|
|
MAX_AUDIO_DEV_LEN);
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* volume knob */
|
|
if (w->type == COP_AWTYPE_VOLUME_KNOB &&
|
|
w->d.volume.cap & COP_VKCAP_DELTA) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: volume knob %s\n", __func__, w->name));
|
|
strlcpy(d->label.name, w->name, sizeof(d->label.name));
|
|
d->type = AUDIO_MIXER_VALUE;
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
m->target = MI_TARGET_VOLUME;
|
|
d->un.v.num_channels = 1;
|
|
d->un.v.units.name[0] = 0;
|
|
d->un.v.delta =
|
|
MIXER_DELTA(COP_VKCAP_NUMSTEPS(w->d.volume.cap));
|
|
this->nmixers++;
|
|
}
|
|
}
|
|
|
|
/* if the codec has multiple DAC groups, create "playback.mode" */
|
|
if (this->dacs.ngroups > 1) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: create playback.mode\n", __func__));
|
|
strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
d->mixer_class = AZ_CLASS_PLAYBACK;
|
|
m->target = MI_TARGET_DAC;
|
|
for (i = 0; i < this->dacs.ngroups && i < 32; i++) {
|
|
d->un.e.member[i].ord = i;
|
|
for (j = 0; j < this->dacs.groups[i].nconv; j++) {
|
|
if (j * 2 >= MAX_AUDIO_DEV_LEN)
|
|
break;
|
|
snprintf(d->un.e.member[i].label.name + j*2,
|
|
MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
|
|
this->dacs.groups[i].conv[j]);
|
|
}
|
|
}
|
|
d->un.e.num_mem = i;
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* if the codec has multiple ADC groups, create "record.mode" */
|
|
if (this->adcs.ngroups > 1) {
|
|
MIXER_REG_PROLOG;
|
|
GMIDPRINTF(("%s: create record.mode\n", __func__));
|
|
strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
m->target = MI_TARGET_ADC;
|
|
for (i = 0; i < this->adcs.ngroups && i < 32; i++) {
|
|
d->un.e.member[i].ord = i;
|
|
for (j = 0; j < this->adcs.groups[i].nconv; j++) {
|
|
if (j * 2 >= MAX_AUDIO_DEV_LEN)
|
|
break;
|
|
snprintf(d->un.e.member[i].label.name + j*2,
|
|
MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
|
|
this->adcs.groups[i].conv[j]);
|
|
}
|
|
}
|
|
d->un.e.num_mem = i;
|
|
this->nmixers++;
|
|
}
|
|
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
generic_mixer_cat_names(char *dst, size_t dstsize,
|
|
const char *str1, const char *str2, const char *str3)
|
|
{
|
|
const char *last2;
|
|
size_t len1, len2, len3, total;
|
|
|
|
len1 = strlen(str1);
|
|
len2 = strlen(str2);
|
|
len3 = strlen(str3);
|
|
total = len1 + 1 + len2 + 1 + len3 + 1;
|
|
if (total - (len3 - 1) <= dstsize) {
|
|
snprintf(dst, dstsize, "%s.%s.%s", str1, str2, str3);
|
|
return;
|
|
}
|
|
last2 = len2 > 2 ? str2 + len2 - 2 : str2;
|
|
if (len2 > 4) {
|
|
snprintf(dst, dstsize, "%s.%.2s%s.%s", str1, str2, last2, str3);
|
|
return;
|
|
}
|
|
snprintf(dst, dstsize, "%s.%s.%s", str1, last2, str3);
|
|
}
|
|
|
|
static int
|
|
generic_mixer_ensure_capacity(codec_t *this, size_t newsize)
|
|
{
|
|
size_t newmax;
|
|
void *newbuf;
|
|
|
|
if (this->maxmixers >= newsize)
|
|
return 0;
|
|
newmax = this->maxmixers + 10;
|
|
if (newmax < newsize)
|
|
newmax = newsize;
|
|
newbuf = realloc(this->mixers, sizeof(mixer_item_t) * newmax, M_DEVBUF,
|
|
M_ZERO | M_NOWAIT);
|
|
if (newbuf == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
this->mixers = newbuf;
|
|
/* realloc(9) doesn't clear expanded area even if M_ZERO. */
|
|
memset(&this->mixers[this->maxmixers], 0,
|
|
sizeof(mixer_item_t) * (newmax - this->maxmixers));
|
|
this->maxmixers = newmax;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_fix_indexes(codec_t *this)
|
|
{
|
|
int i;
|
|
mixer_devinfo_t *d;
|
|
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
d = &this->mixers[i].devinfo;
|
|
#ifdef DIAGNOSTIC
|
|
if (d->index != 0 && d->index != i)
|
|
aprint_error("%s: index mismatch %d %d\n", __func__,
|
|
d->index, i);
|
|
#endif
|
|
d->index = i;
|
|
if (d->prev == 0)
|
|
d->prev = AUDIO_MIXER_LAST;
|
|
if (d->next == 0)
|
|
d->next = AUDIO_MIXER_LAST;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_default(codec_t *this)
|
|
{
|
|
int i;
|
|
mixer_item_t *m;
|
|
/* unmute all */
|
|
DPRINTF(("%s: unmute\n", __func__));
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
mixer_ctrl_t mc;
|
|
|
|
m = &this->mixers[i];
|
|
if (!IS_MI_TARGET_INAMP(m->target) &&
|
|
m->target != MI_TARGET_OUTAMP)
|
|
continue;
|
|
if (m->devinfo.type != AUDIO_MIXER_ENUM)
|
|
continue;
|
|
mc.dev = i;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 0;
|
|
generic_mixer_set(this, m->nid, m->target, &mc);
|
|
}
|
|
|
|
/*
|
|
* For bidirectional pins, make the default `output'
|
|
*/
|
|
DPRINTF(("%s: process bidirectional pins\n", __func__));
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
mixer_ctrl_t mc;
|
|
|
|
m = &this->mixers[i];
|
|
if (m->target != MI_TARGET_PINDIR)
|
|
continue;
|
|
mc.dev = i;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* output */
|
|
generic_mixer_set(this, m->nid, m->target, &mc);
|
|
}
|
|
|
|
/* set unextreme volume */
|
|
DPRINTF(("%s: set volume\n", __func__));
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
mixer_ctrl_t mc;
|
|
|
|
m = &this->mixers[i];
|
|
if (!IS_MI_TARGET_INAMP(m->target) &&
|
|
m->target != MI_TARGET_OUTAMP &&
|
|
m->target != MI_TARGET_VOLUME)
|
|
continue;
|
|
if (m->devinfo.type != AUDIO_MIXER_VALUE)
|
|
continue;
|
|
mc.dev = i;
|
|
mc.type = AUDIO_MIXER_VALUE;
|
|
mc.un.value.num_channels = 1;
|
|
mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
|
|
if (m->target != MI_TARGET_VOLUME &&
|
|
WIDGET_CHANNELS(&this->w[m->nid]) == 2) {
|
|
mc.un.value.num_channels = 2;
|
|
mc.un.value.level[1] = AUDIO_MAX_GAIN / 2;
|
|
}
|
|
generic_mixer_set(this, m->nid, m->target, &mc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_pin_sense(codec_t *this)
|
|
{
|
|
typedef enum {
|
|
PIN_DIR_IN,
|
|
PIN_DIR_OUT,
|
|
PIN_DIR_MIC
|
|
} pintype_t;
|
|
const widget_t *w;
|
|
int i;
|
|
|
|
FOR_EACH_WIDGET(this, i) {
|
|
pintype_t pintype = PIN_DIR_IN;
|
|
|
|
w = &this->w[i];
|
|
if (w->type != COP_AWTYPE_PIN_COMPLEX)
|
|
continue;
|
|
if (!(w->d.pin.cap & COP_PINCAP_INPUT))
|
|
pintype = PIN_DIR_OUT;
|
|
if (!(w->d.pin.cap & COP_PINCAP_OUTPUT))
|
|
pintype = PIN_DIR_IN;
|
|
|
|
switch (w->d.pin.device) {
|
|
case CORB_CD_LINEOUT:
|
|
case CORB_CD_SPEAKER:
|
|
case CORB_CD_HEADPHONE:
|
|
case CORB_CD_SPDIFOUT:
|
|
case CORB_CD_DIGITALOUT:
|
|
pintype = PIN_DIR_OUT;
|
|
break;
|
|
case CORB_CD_CD:
|
|
case CORB_CD_LINEIN:
|
|
pintype = PIN_DIR_IN;
|
|
break;
|
|
case CORB_CD_MICIN:
|
|
pintype = PIN_DIR_MIC;
|
|
break;
|
|
}
|
|
|
|
switch (pintype) {
|
|
case PIN_DIR_IN:
|
|
this->comresp(this, w->nid,
|
|
CORB_SET_PIN_WIDGET_CONTROL,
|
|
CORB_PWC_INPUT, NULL);
|
|
break;
|
|
case PIN_DIR_OUT:
|
|
this->comresp(this, w->nid,
|
|
CORB_SET_PIN_WIDGET_CONTROL,
|
|
CORB_PWC_OUTPUT, NULL);
|
|
break;
|
|
case PIN_DIR_MIC:
|
|
this->comresp(this, w->nid,
|
|
CORB_SET_PIN_WIDGET_CONTROL,
|
|
CORB_PWC_INPUT|CORB_PWC_VREF_80, NULL);
|
|
break;
|
|
}
|
|
|
|
if (w->d.pin.cap & COP_PINCAP_EAPD) {
|
|
uint32_t result;
|
|
int err;
|
|
|
|
err = this->comresp(this, w->nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
continue;
|
|
result &= 0xff;
|
|
result |= CORB_EAPD_EAPD;
|
|
err = this->comresp(this, w->nid,
|
|
CORB_SET_EAPD_BTL_ENABLE, result, &result);
|
|
if (err)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_widget_name(const codec_t *this, widget_t *w)
|
|
{
|
|
const char *name = NULL, *grossloc = "", *geoloc = "";
|
|
uint8_t grosslocval, geolocval;
|
|
|
|
if (w->type != COP_AWTYPE_PIN_COMPLEX)
|
|
return 0;
|
|
|
|
switch (w->d.pin.device) {
|
|
case CORB_CD_LINEOUT: name = "lineout"; break;
|
|
case CORB_CD_SPEAKER: name = "spkr"; break;
|
|
case CORB_CD_HEADPHONE: name = "hp"; break;
|
|
case CORB_CD_CD: name = AudioNcd; break;
|
|
case CORB_CD_SPDIFOUT: name = "spdifout"; break;
|
|
case CORB_CD_DIGITALOUT: name = "digout"; break;
|
|
case CORB_CD_MODEMLINE: name = "modemline"; break;
|
|
case CORB_CD_MODEMHANDSET: name = "modemhset"; break;
|
|
case CORB_CD_LINEIN: name = "linein"; break;
|
|
case CORB_CD_AUX: name = AudioNaux; break;
|
|
case CORB_CD_MICIN: name = AudioNmicrophone; break;
|
|
case CORB_CD_TELEPHONY: name = "telephony"; break;
|
|
case CORB_CD_SPDIFIN: name = "spdifin"; break;
|
|
case CORB_CD_DIGITALIN: name = "digin"; break;
|
|
case CORB_CD_DEVICE_OTHER: name = "reserved"; break;
|
|
default: name = "unused"; break;
|
|
}
|
|
|
|
grosslocval = ((w->d.pin.config & CORB_CD_LOCATION_MASK) >> 24) & 0xf;
|
|
geolocval = (w->d.pin.config & CORB_CD_LOCATION_MASK) >> 28;
|
|
|
|
switch (geolocval) {
|
|
case 0x00: /* external on primary chassis */
|
|
case 0x10: /* external on separate chassis */
|
|
geoloc = (geolocval == 0x00 ? "" : "d");
|
|
switch (grosslocval) {
|
|
case 0x00: grossloc = ""; break; /* N/A */
|
|
case 0x01: grossloc = ""; break; /* rear */
|
|
case 0x02: grossloc = ".front"; break; /* front */
|
|
case 0x03: grossloc = ".left"; break; /* left */
|
|
case 0x04: grossloc = ".right"; break; /* right */
|
|
case 0x05: grossloc = ".top"; break; /* top */
|
|
case 0x06: grossloc = ".bottom"; break; /* bottom */
|
|
case 0x07: grossloc = ".rearpnl"; break; /* rear panel */
|
|
case 0x08: grossloc = ".drivebay"; break; /* drive bay */
|
|
default: grossloc = ""; break;
|
|
}
|
|
break;
|
|
case 0x01:
|
|
geoloc = "i";
|
|
switch (grosslocval) {
|
|
case 0x00: grossloc = ""; break; /* N/A */
|
|
case 0x07: grossloc = ".riser"; break; /* riser */
|
|
case 0x08: grossloc = ".hdmi"; break; /* hdmi */
|
|
default: grossloc = ""; break;
|
|
}
|
|
break;
|
|
default:
|
|
geoloc = "o";
|
|
switch (grosslocval) {
|
|
case 0x00: grossloc = ""; break; /* N/A */
|
|
case 0x06: grossloc = ".bottom"; break; /* bottom */
|
|
case 0x07: grossloc = ".lidin"; break; /* lid inside */
|
|
case 0x08: grossloc = ".lidout"; break; /* lid outside */
|
|
default: grossloc = ""; break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
snprintf(w->name, sizeof(w->name), "%s%s%s", geoloc, name, grossloc);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_create_virtual(codec_t *this)
|
|
{
|
|
mixer_item_t *m;
|
|
mixer_devinfo_t *d;
|
|
convgroup_t *cgdac = &this->dacs.groups[0];
|
|
convgroup_t *cgadc = &this->adcs.groups[0];
|
|
int i, err, mdac, madc, mmaster;
|
|
|
|
/* Clear mixer indexes, to make generic_mixer_fix_index happy */
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
d = &this->mixers[i].devinfo;
|
|
d->index = d->prev = d->next = 0;
|
|
}
|
|
|
|
mdac = madc = mmaster = -1;
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
if (this->mixers[i].devinfo.type != AUDIO_MIXER_VALUE)
|
|
continue;
|
|
if (mdac < 0 && this->dacs.ngroups > 0 && cgdac->nconv > 0) {
|
|
if (this->mixers[i].nid == cgdac->conv[0])
|
|
mdac = mmaster = i;
|
|
}
|
|
if (madc < 0 && this->adcs.ngroups > 0 && cgadc->nconv > 0) {
|
|
if (this->mixers[i].nid == cgadc->conv[0])
|
|
madc = i;
|
|
}
|
|
}
|
|
|
|
if (mdac == -1) {
|
|
/*
|
|
* no volume mixer found on the DAC; enumerate peer widgets
|
|
* and try to find a volume mixer on them
|
|
*/
|
|
widget_t *w;
|
|
int j;
|
|
FOR_EACH_WIDGET(this, i) {
|
|
w = &this->w[i];
|
|
for (j = 0; j < w->nconnections; j++)
|
|
if (w->connections[j] == cgdac->conv[0])
|
|
break;
|
|
|
|
if (j == w->nconnections)
|
|
continue;
|
|
|
|
for (j = 0; j < this->nmixers; j++) {
|
|
if (this->mixers[j].devinfo.type !=
|
|
AUDIO_MIXER_VALUE)
|
|
continue;
|
|
if (this->mixers[j].nid == w->nid) {
|
|
mdac = mmaster = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mdac == -1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mdac >= 0) {
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
if (err)
|
|
return err;
|
|
m = &this->mixers[this->nmixers];
|
|
d = &m->devinfo;
|
|
memcpy(m, &this->mixers[mmaster], sizeof(*m));
|
|
d->mixer_class = AZ_CLASS_OUTPUT;
|
|
snprintf(d->label.name, sizeof(d->label.name), AudioNmaster);
|
|
this->nmixers++;
|
|
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
if (err)
|
|
return err;
|
|
m = &this->mixers[this->nmixers];
|
|
d = &m->devinfo;
|
|
memcpy(m, &this->mixers[mdac], sizeof(*m));
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
snprintf(d->label.name, sizeof(d->label.name), AudioNdac);
|
|
this->nmixers++;
|
|
}
|
|
|
|
if (madc >= 0) {
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
if (err)
|
|
return err;
|
|
m = &this->mixers[this->nmixers];
|
|
d = &m->devinfo;
|
|
memcpy(m, &this->mixers[madc], sizeof(*m));
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
snprintf(d->label.name, sizeof(d->label.name), AudioNvolume);
|
|
this->nmixers++;
|
|
}
|
|
|
|
generic_mixer_fix_indexes(this);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_autoinit(codec_t *this)
|
|
{
|
|
generic_mixer_init(this);
|
|
generic_mixer_create_virtual(this);
|
|
generic_mixer_pin_sense(this);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
{
|
|
return generic_mixer_widget_name(this, w);
|
|
}
|
|
|
|
static int
|
|
generic_mixer_delete(codec_t *this)
|
|
{
|
|
if (this->mixers == NULL)
|
|
return 0;
|
|
free(this->mixers, M_DEVBUF);
|
|
this->mixers = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param mc mc->type must be set by the caller before the call
|
|
*/
|
|
static int
|
|
generic_mixer_get(const codec_t *this, nid_t nid, int target, mixer_ctrl_t *mc)
|
|
{
|
|
uint32_t result;
|
|
nid_t n;
|
|
int err;
|
|
|
|
/* inamp mute */
|
|
if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
|
|
MI_TARGET_INAMP(target), &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
|
|
}
|
|
|
|
/* inamp gain */
|
|
else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
|
|
MI_TARGET_INAMP(target), &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.level[0] = generic_mixer_from_device_value(this,
|
|
nid, target, CORB_GAGM_GAIN(result));
|
|
if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR ||
|
|
this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) {
|
|
n = this->w[nid].connections[MI_TARGET_INAMP(target)];
|
|
#ifdef AZALIA_DEBUG
|
|
if (!VALID_WIDGET_NID(n, this)) {
|
|
DPRINTF(("%s: invalid target: nid=%d nconn=%d index=%d\n",
|
|
__func__, nid, this->w[nid].nconnections,
|
|
MI_TARGET_INAMP(target)));
|
|
return EINVAL;
|
|
}
|
|
#endif
|
|
} else {
|
|
n = nid;
|
|
}
|
|
mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
|
|
if (mc->un.value.num_channels == 2) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
|
|
CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
|
|
&result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.level[1] = generic_mixer_from_device_value
|
|
(this, nid, target, CORB_GAGM_GAIN(result));
|
|
}
|
|
}
|
|
|
|
/* outamp mute */
|
|
else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
|
|
}
|
|
|
|
/* outamp gain */
|
|
else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.level[0] = generic_mixer_from_device_value(this,
|
|
nid, target, CORB_GAGM_GAIN(result));
|
|
mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]);
|
|
if (mc->un.value.num_channels == 2) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.level[1] = generic_mixer_from_device_value
|
|
(this, nid, target, CORB_GAGM_GAIN(result));
|
|
}
|
|
}
|
|
|
|
/* selection */
|
|
else if (target == MI_TARGET_CONNLIST) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
|
|
if (err)
|
|
return err;
|
|
result = CORB_CSC_INDEX(result);
|
|
if (!VALID_WIDGET_NID(this->w[nid].connections[result], this))
|
|
mc->un.ord = -1;
|
|
else
|
|
mc->un.ord = result;
|
|
}
|
|
|
|
/* pin I/O */
|
|
else if (target == MI_TARGET_PINDIR) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_PWC_OUTPUT ? 1 : 0;
|
|
}
|
|
|
|
/* pin headphone-boost */
|
|
else if (target == MI_TARGET_PINBOOST) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
|
|
}
|
|
|
|
/* DAC group selection */
|
|
else if (target == MI_TARGET_DAC) {
|
|
mc->un.ord = this->dacs.cur;
|
|
}
|
|
|
|
/* ADC selection */
|
|
else if (target == MI_TARGET_ADC) {
|
|
mc->un.ord = this->adcs.cur;
|
|
}
|
|
|
|
/* Volume knob */
|
|
else if (target == MI_TARGET_VOLUME) {
|
|
err = this->comresp(this, nid, CORB_GET_VOLUME_KNOB,
|
|
0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.level[0] = generic_mixer_from_device_value(this,
|
|
nid, target, CORB_VKNOB_VOLUME(result));
|
|
mc->un.value.num_channels = 1;
|
|
}
|
|
|
|
/* S/PDIF */
|
|
else if (target == MI_TARGET_SPDIF) {
|
|
err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
|
|
0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO);
|
|
} else if (target == MI_TARGET_SPDIF_CC) {
|
|
err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
|
|
0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.value.num_channels = 1;
|
|
mc->un.value.level[0] = CORB_DCC_CC(result);
|
|
}
|
|
|
|
/* EAPD */
|
|
else if (target == MI_TARGET_EAPD) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_EAPD_EAPD ? 1 : 0;
|
|
}
|
|
|
|
/* Balanced I/O */
|
|
else if (target == MI_TARGET_BALANCE) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_EAPD_BTL ? 1 : 0;
|
|
}
|
|
|
|
/* LR-Swap */
|
|
else if (target == MI_TARGET_LRSWAP) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
mc->un.ord = result & CORB_EAPD_LRSWAP ? 1 : 0;
|
|
}
|
|
|
|
else {
|
|
aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
|
|
__func__, target);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
|
|
{
|
|
uint32_t result, value;
|
|
int err;
|
|
|
|
/* inamp mute */
|
|
if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
|
|
/* We have to set stereo mute separately to keep each gain value. */
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
|
|
MI_TARGET_INAMP(target), &result);
|
|
if (err)
|
|
return err;
|
|
value = CORB_AGM_INPUT | CORB_AGM_LEFT |
|
|
(target << CORB_AGM_INDEX_SHIFT) |
|
|
CORB_GAGM_GAIN(result);
|
|
if (mc->un.ord)
|
|
value |= CORB_AGM_MUTE;
|
|
err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
|
|
value, &result);
|
|
if (err)
|
|
return err;
|
|
if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
|
|
CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
|
|
&result);
|
|
if (err)
|
|
return err;
|
|
value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
|
|
(target << CORB_AGM_INDEX_SHIFT) |
|
|
CORB_GAGM_GAIN(result);
|
|
if (mc->un.ord)
|
|
value |= CORB_AGM_MUTE;
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* inamp gain */
|
|
else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
|
|
if (mc->un.value.num_channels < 1)
|
|
return EINVAL;
|
|
if (!generic_mixer_validate_value(this, nid, target,
|
|
mc->un.value.level[0]))
|
|
return EINVAL;
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
|
|
MI_TARGET_INAMP(target), &result);
|
|
if (err)
|
|
return err;
|
|
value = generic_mixer_to_device_value(this, nid, target,
|
|
mc->un.value.level[0]);
|
|
value = CORB_AGM_INPUT | CORB_AGM_LEFT |
|
|
(target << CORB_AGM_INDEX_SHIFT) |
|
|
(result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
|
|
(value & CORB_AGM_GAIN_MASK);
|
|
err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
|
|
value, &result);
|
|
if (err)
|
|
return err;
|
|
if (mc->un.value.num_channels >= 2 &&
|
|
WIDGET_CHANNELS(&this->w[nid]) == 2) {
|
|
if (!generic_mixer_validate_value(this, nid, target,
|
|
mc->un.value.level[1]))
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
|
|
CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
|
|
&result);
|
|
if (err)
|
|
return err;
|
|
value = generic_mixer_to_device_value(this, nid, target,
|
|
mc->un.value.level[1]);
|
|
value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
|
|
(target << CORB_AGM_INDEX_SHIFT) |
|
|
(result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
|
|
(value & CORB_AGM_GAIN_MASK);
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* outamp mute */
|
|
else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
|
|
if (err)
|
|
return err;
|
|
value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
|
|
if (mc->un.ord)
|
|
value |= CORB_AGM_MUTE;
|
|
err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
|
|
value, &result);
|
|
if (err)
|
|
return err;
|
|
if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
|
|
if (err)
|
|
return err;
|
|
value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
|
|
CORB_GAGM_GAIN(result);
|
|
if (mc->un.ord)
|
|
value |= CORB_AGM_MUTE;
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* outamp gain */
|
|
else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
|
|
if (mc->un.value.num_channels < 1)
|
|
return EINVAL;
|
|
if (!generic_mixer_validate_value(this, nid, target,
|
|
mc->un.value.level[0]))
|
|
return EINVAL;
|
|
err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
|
|
if (err)
|
|
return err;
|
|
value = generic_mixer_to_device_value(this, nid, target,
|
|
mc->un.value.level[0]);
|
|
value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
|
|
(result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
|
|
(value & CORB_AGM_GAIN_MASK);
|
|
err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
|
|
value, &result);
|
|
if (err)
|
|
return err;
|
|
if (mc->un.value.num_channels >= 2 &&
|
|
WIDGET_CHANNELS(&this->w[nid]) == 2) {
|
|
if (!generic_mixer_validate_value(this, nid, target,
|
|
mc->un.value.level[1]))
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
|
|
CORB_GAGM_RIGHT, &result);
|
|
if (err)
|
|
return err;
|
|
value = generic_mixer_to_device_value(this, nid, target,
|
|
mc->un.value.level[1]);
|
|
value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
|
|
(result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
|
|
(value & CORB_AGM_GAIN_MASK);
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* selection */
|
|
else if (target == MI_TARGET_CONNLIST) {
|
|
if (mc->un.ord < 0 ||
|
|
mc->un.ord >= this->w[nid].nconnections ||
|
|
!VALID_WIDGET_NID(this->w[nid].connections[mc->un.ord], this))
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* pin I/O */
|
|
else if (target == MI_TARGET_PINDIR) {
|
|
if (mc->un.ord >= 2)
|
|
return EINVAL;
|
|
if (mc->un.ord == 0) {
|
|
return generic_mixer_pinctrl(this, nid, CORB_PWC_INPUT);
|
|
} else {
|
|
return generic_mixer_pinctrl(
|
|
this, nid, CORB_PWC_OUTPUT);
|
|
}
|
|
}
|
|
|
|
/* pin headphone-boost */
|
|
else if (target == MI_TARGET_PINBOOST) {
|
|
if (mc->un.ord >= 2)
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
|
|
if (err)
|
|
return err;
|
|
if (mc->un.ord == 0) {
|
|
result &= ~CORB_PWC_HEADPHONE;
|
|
} else {
|
|
result |= CORB_PWC_HEADPHONE;
|
|
}
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_PIN_WIDGET_CONTROL, result, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* DAC group selection */
|
|
else if (target == MI_TARGET_DAC) {
|
|
if (this->running)
|
|
return EBUSY;
|
|
if (mc->un.ord >= this->dacs.ngroups)
|
|
return EINVAL;
|
|
return azalia_codec_construct_format(this,
|
|
mc->un.ord, this->adcs.cur);
|
|
}
|
|
|
|
/* ADC selection */
|
|
else if (target == MI_TARGET_ADC) {
|
|
if (this->running)
|
|
return EBUSY;
|
|
if (mc->un.ord >= this->adcs.ngroups)
|
|
return EINVAL;
|
|
return azalia_codec_construct_format(this,
|
|
this->dacs.cur, mc->un.ord);
|
|
}
|
|
|
|
/* Volume knob */
|
|
else if (target == MI_TARGET_VOLUME) {
|
|
if (mc->un.value.num_channels != 1)
|
|
return EINVAL;
|
|
if (!generic_mixer_validate_value(this, nid,
|
|
target, mc->un.value.level[0]))
|
|
return EINVAL;
|
|
value = generic_mixer_to_device_value(this, nid, target,
|
|
mc->un.value.level[0]) | CORB_VKNOB_DIRECT;
|
|
err = this->comresp(this, nid, CORB_SET_VOLUME_KNOB,
|
|
value, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* S/PDIF */
|
|
else if (target == MI_TARGET_SPDIF) {
|
|
err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
|
|
0, &result);
|
|
result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO;
|
|
result |= mc->un.mask & 0xff & ~CORB_DCC_DIGEN;
|
|
err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L,
|
|
result, NULL);
|
|
if (err)
|
|
return err;
|
|
} else if (target == MI_TARGET_SPDIF_CC) {
|
|
if (mc->un.value.num_channels != 1)
|
|
return EINVAL;
|
|
if (mc->un.value.level[0] > 127)
|
|
return EINVAL;
|
|
err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_H,
|
|
mc->un.value.level[0], NULL);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* EAPD */
|
|
else if (target == MI_TARGET_EAPD) {
|
|
if (mc->un.ord >= 2)
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
result &= 0xff;
|
|
if (mc->un.ord == 0) {
|
|
result &= ~CORB_EAPD_EAPD;
|
|
} else {
|
|
result |= CORB_EAPD_EAPD;
|
|
}
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_EAPD_BTL_ENABLE, result, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Balanced I/O */
|
|
else if (target == MI_TARGET_BALANCE) {
|
|
if (mc->un.ord >= 2)
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
result &= 0xff;
|
|
if (mc->un.ord == 0) {
|
|
result &= ~CORB_EAPD_BTL;
|
|
} else {
|
|
result |= CORB_EAPD_BTL;
|
|
}
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_EAPD_BTL_ENABLE, result, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* LR-Swap */
|
|
else if (target == MI_TARGET_LRSWAP) {
|
|
if (mc->un.ord >= 2)
|
|
return EINVAL;
|
|
err = this->comresp(this, nid,
|
|
CORB_GET_EAPD_BTL_ENABLE, 0, &result);
|
|
if (err)
|
|
return err;
|
|
result &= 0xff;
|
|
if (mc->un.ord == 0) {
|
|
result &= ~CORB_EAPD_LRSWAP;
|
|
} else {
|
|
result |= CORB_EAPD_LRSWAP;
|
|
}
|
|
err = this->comresp(this, nid,
|
|
CORB_SET_EAPD_BTL_ENABLE, result, &result);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
else {
|
|
aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
|
|
__func__, target);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generic_mixer_pinctrl(codec_t *this, nid_t nid, uint32_t value)
|
|
{
|
|
int err;
|
|
uint32_t result;
|
|
|
|
err = this->comresp(this, nid, CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
|
|
if (err)
|
|
return err;
|
|
result &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT);
|
|
result |= value & (CORB_PWC_OUTPUT | CORB_PWC_INPUT);
|
|
return this->comresp(this, nid,
|
|
CORB_SET_PIN_WIDGET_CONTROL, result, NULL);
|
|
}
|
|
|
|
static u_char
|
|
generic_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
|
|
uint32_t dv)
|
|
{
|
|
#ifdef MAX_VOLUME_255
|
|
uint32_t dmax;
|
|
|
|
if (IS_MI_TARGET_INAMP(target))
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
|
|
else if (target == MI_TARGET_OUTAMP)
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
|
|
else if (target == MI_TARGET_VOLUME)
|
|
dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
|
|
else {
|
|
printf("unknown target: %d\n", target);
|
|
dmax = 127;
|
|
}
|
|
return dv * MIXER_DELTA(dmax);
|
|
#else
|
|
return dv;
|
|
#endif
|
|
}
|
|
|
|
static uint32_t
|
|
generic_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
|
|
u_char uv)
|
|
{
|
|
#ifdef MAX_VOLUME_255
|
|
uint32_t dmax;
|
|
|
|
if (IS_MI_TARGET_INAMP(target))
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
|
|
else if (target == MI_TARGET_OUTAMP)
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
|
|
else if (target == MI_TARGET_VOLUME)
|
|
dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
|
|
else {
|
|
printf("unknown target: %d\n", target);
|
|
dmax = 127;
|
|
}
|
|
return uv / MIXER_DELTA(dmax);
|
|
#else
|
|
return uv;
|
|
#endif
|
|
}
|
|
|
|
static uint32_t
|
|
generic_mixer_max(const codec_t *this, nid_t nid,
|
|
int target)
|
|
{
|
|
#ifdef MAX_VOLUME_255
|
|
return AUDIO_MAX_GAIN;
|
|
#else
|
|
uint32_t dmax;
|
|
|
|
if (IS_MI_TARGET_INAMP(target))
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
|
|
else if (target == MI_TARGET_OUTAMP)
|
|
dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
|
|
else if (target == MI_TARGET_VOLUME)
|
|
dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap);
|
|
return dmax;
|
|
#endif
|
|
}
|
|
|
|
static bool
|
|
generic_mixer_validate_value(const codec_t *this, nid_t nid,
|
|
int target, u_char uv)
|
|
{
|
|
#ifdef MAX_VOLUME_255
|
|
return TRUE;
|
|
#else
|
|
return uv <= generic_mixer_max(this, nid, target);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
generic_set_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
if (mc->type != m->devinfo.type)
|
|
return EINVAL;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0; /* nothing to do */
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
static int
|
|
generic_get_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
mc->type = m->devinfo.type;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0; /* nothing to do */
|
|
return generic_mixer_get(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC260
|
|
*
|
|
* Fujitsu LOOX T70M/T
|
|
* Internal Speaker: 0x10
|
|
* Front Headphone: 0x14
|
|
* Front mic: 0x12
|
|
* ---------------------------------------------------------------- */
|
|
|
|
#define ALC260_FUJITSU_ID 0x132610cf
|
|
#define ALC260_EVENT_HP 0
|
|
#define ALC260_EXTRA_MASTER 0
|
|
static const mixer_item_t alc260_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
|
|
{{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNmono".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
|
{{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x12, MI_TARGET_PINDIR},
|
|
{{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x13, MI_TARGET_PINDIR},
|
|
{{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {"line1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x14, MI_TARGET_PINDIR},
|
|
{{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {"line2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x15, MI_TARGET_PINDIR},
|
|
|
|
{{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
|
|
{{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(1)},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(1)},
|
|
{{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(2)},
|
|
{{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(2)},
|
|
{{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(3)},
|
|
{{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(3)},
|
|
{{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
|
|
|
|
{{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
|
|
{{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}},
|
|
0x04, MI_TARGET_CONNLIST},
|
|
{{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={6, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
|
|
{{"line2", 0}, 3}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}},
|
|
0x05, MI_TARGET_CONNLIST},
|
|
{{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}},
|
|
0, MI_TARGET_ADC},
|
|
};
|
|
|
|
static const mixer_item_t alc260_loox_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */
|
|
{{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
|
|
{{0, {AudioNmicrophone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
|
|
|
|
{{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}}}}, 0x04, MI_TARGET_CONNLIST},
|
|
{{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={3, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}},
|
|
0x05, MI_TARGET_CONNLIST},
|
|
{{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
|
|
{{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}},
|
|
0, MI_TARGET_ADC},
|
|
};
|
|
|
|
static int
|
|
alc260_mixer_init(codec_t *this)
|
|
{
|
|
const mixer_item_t *mi;
|
|
mixer_ctrl_t mc;
|
|
uint32_t value;
|
|
|
|
switch (this->subid) {
|
|
case ALC260_FUJITSU_ID:
|
|
this->nmixers = __arraycount(alc260_loox_mixer_items);
|
|
mi = alc260_loox_mixer_items;
|
|
break;
|
|
default:
|
|
this->nmixers = __arraycount(alc260_mixer_items);
|
|
mi = alc260_mixer_items;
|
|
}
|
|
this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
|
|
M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, mi, sizeof(mixer_item_t) * this->nmixers);
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1; /* no need for generic_mixer_set() */
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* lineout */
|
|
generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* headphones */
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x12, MI_TARGET_PINDIR, &mc); /* mic1 */
|
|
generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* mic2 */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
|
|
generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* line2 */
|
|
mc.un.ord = 0; /* mute: off */
|
|
generic_mixer_set(this, 0x08, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x08, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x09, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x09, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(1), &mc);
|
|
if (this->subid == ALC260_FUJITSU_ID) {
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */
|
|
mc.un.ord = 4; /* connlist: cd */
|
|
generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc);
|
|
/* setup a unsolicited event for the headphones */
|
|
this->comresp(this, 0x14, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | ALC260_EVENT_HP, NULL);
|
|
this->extra[ALC260_EXTRA_MASTER] = 0; /* unmute */
|
|
/* If the headphone presents, mute the internal speaker */
|
|
this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
|
|
mc.un.ord = value & CORB_PS_PRESENCE ? 1 : 0;
|
|
generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
|
|
this->get_port = alc260_get_port;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc260_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{1, {0x02}}, /* analog 2ch */
|
|
{1, {0x03}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 3,
|
|
{{1, {0x04}}, /* analog 2ch */
|
|
{1, {0x05}}, /* analog 2ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc260_set_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
mixer_ctrl_t mc2;
|
|
uint32_t value;
|
|
int err;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
if (mc->type != m->devinfo.type)
|
|
return EINVAL;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0;
|
|
if (m->nid == 0x08 && m->target == MI_TARGET_OUTAMP) {
|
|
DPRINTF(("%s: hook for outputs.master\n", __func__));
|
|
err = generic_mixer_set(this, m->nid, m->target, mc);
|
|
if (!err) {
|
|
generic_mixer_set(this, 0x09, m->target, mc);
|
|
mc2 = *mc;
|
|
mc2.un.value.num_channels = 1;
|
|
mc2.un.value.level[0] = (mc2.un.value.level[0]
|
|
+ mc2.un.value.level[1]) / 2;
|
|
generic_mixer_set(this, 0x0a, m->target, &mc2);
|
|
}
|
|
return err;
|
|
} else if (m->nid == 0x08 && m->target == MI_TARGET_INAMP(0)) {
|
|
DPRINTF(("%s: hook for inputs.dac.mute\n", __func__));
|
|
err = generic_mixer_set(this, m->nid, m->target, mc);
|
|
if (!err) {
|
|
generic_mixer_set(this, 0x09, m->target, mc);
|
|
generic_mixer_set(this, 0x0a, m->target, mc);
|
|
}
|
|
return err;
|
|
} else if (m->nid == 0x04 &&
|
|
m->target == MI_TARGET_CONNLIST &&
|
|
m->devinfo.un.e.num_mem == 2) {
|
|
if (1 <= mc->un.ord && mc->un.ord <= 3)
|
|
return EINVAL;
|
|
} else if (m->nid == 0x05 &&
|
|
m->target == MI_TARGET_CONNLIST &&
|
|
m->devinfo.un.e.num_mem == 3) {
|
|
if (1 <= mc->un.ord && mc->un.ord <= 3)
|
|
return EINVAL;
|
|
} else if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 &&
|
|
m->target == MI_TARGET_OUTAMP) {
|
|
if (mc->un.ord != 0 && mc->un.ord != 1)
|
|
return EINVAL;
|
|
this->extra[ALC260_EXTRA_MASTER] = mc->un.ord;
|
|
err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
return err;
|
|
if (!(value & CORB_PS_PRESENCE)) {
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
|
}
|
|
return 0;
|
|
}
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
static int
|
|
alc260_get_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
mc->type = m->devinfo.type;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0;
|
|
if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 &&
|
|
m->target == MI_TARGET_OUTAMP) {
|
|
mc->un.ord = this->extra[ALC260_EXTRA_MASTER];
|
|
return 0;
|
|
}
|
|
return generic_mixer_get(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
static int
|
|
alc260_unsol_event(codec_t *this, int tag)
|
|
{
|
|
int err;
|
|
uint32_t value;
|
|
mixer_ctrl_t mc;
|
|
|
|
switch (tag) {
|
|
case ALC260_EVENT_HP:
|
|
err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: headphone has been inserted.\n", __func__));
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
|
|
} else {
|
|
DPRINTF(("%s: headphone has been pulled out.\n", __func__));
|
|
mc.un.ord = this->extra[ALC260_EXTRA_MASTER];
|
|
generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC262
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc262_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
{
|
|
switch (nid) {
|
|
case 0x0c:
|
|
strlcpy(w->name, AudioNmaster, sizeof(w->name));
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC268
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc268_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 1,
|
|
{{2, {0x02, 0x03}}}}; /* analog 4ch */
|
|
static const convgroupset_t adcs = {
|
|
-1, 1,
|
|
{{2, {0x08, 0x07}}}}; /* analog 4ch */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC662-GR
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc662_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 1,
|
|
{{3, {0x02, 0x03, 0x04}}}}; /* analog 6ch */
|
|
static const convgroupset_t adcs = {
|
|
-1, 1,
|
|
{{2, {0x09, 0x08}}}}; /* analog 4ch */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC861
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc861_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */
|
|
{1, {0x07}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 1,
|
|
{{1, {0x08}}}}; /* analog 2ch */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC861-VD-GR
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc861vdgr_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 1,
|
|
{{1, {0x09}}}}; /* analog 2ch */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC880
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t alc880_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
|
|
{{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}}, 0x08, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x08, MI_TARGET_INAMP(0)},
|
|
|
|
{{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2},
|
|
{{"line2", 0}, 3}, {{AudioNcd, 0}, 4},
|
|
{{AudioNmixerout, 0}, 5}}}}, 0x09, MI_TARGET_CONNLIST},
|
|
{{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x09, MI_TARGET_INAMP(0)},
|
|
|
|
{{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(3)},
|
|
{{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(3)},
|
|
{{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(5)},
|
|
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0c, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0d, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0e, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0f, MI_TARGET_OUTAMP},
|
|
#if 0 /* The followings are useless. */
|
|
{{0, {AudioNsurround".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
|
|
{{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
|
|
{{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
|
|
#endif
|
|
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x19, MI_TARGET_OUTAMP},
|
|
{{0, {"mic2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x19, MI_TARGET_PINBOOST},
|
|
{{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x19, MI_TARGET_PINDIR},
|
|
{{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
|
|
{{0, {"line1.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST},
|
|
{{0, {"line1.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x1a, MI_TARGET_PINDIR},
|
|
{{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
|
{{0, {"line2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
|
{{0, {"line2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x1b, MI_TARGET_PINDIR},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC},
|
|
};
|
|
|
|
static int
|
|
alc880_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(alc880_mixer_items);
|
|
this->mixers = malloc(sizeof(alc880_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, alc880_mixer_items, sizeof(alc880_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 0; /* mute: off */
|
|
generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(0), &mc); /* dac->front */
|
|
generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(1), &mc); /* mixer->front */
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc); /* dac->surround */
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc); /* dac->clfe */
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc); /* dac->side */
|
|
mc.un.ord = 1; /* mute: on */
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc); /* mixer->surround */
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc); /* mixer->clfe */
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc); /* mixer->side */
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* front */
|
|
generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* surround */
|
|
generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc); /* clfe */
|
|
generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc); /* side */
|
|
generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc); /* mic2 */
|
|
generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc); /* line2 */
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc); /* mic1 */
|
|
generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc); /* line1 */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc880_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x08, 0x09}}, /* analog 4ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC882
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t alc882_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
/* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
|
|
{{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
|
|
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
|
|
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
|
|
|
|
/* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17,0xb */
|
|
#define ALC882_MIC1 0x001
|
|
#define ALC882_MIC2 0x002
|
|
#define ALC882_LINE 0x004
|
|
#define ALC882_CD 0x010
|
|
#define ALC882_BEEP 0x020
|
|
#define ALC882_MIX 0x400
|
|
#define ALC882_MASK 0x437
|
|
{{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x07, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
|
|
0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
|
|
{{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
|
|
{{AudioNspeaker, 0}, ALC882_BEEP},
|
|
{{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x24, -1},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
|
|
0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
|
|
{{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
|
|
{{AudioNspeaker, 0}, ALC882_BEEP},
|
|
{{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1},
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
|
|
0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
|
|
{{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
|
|
{{AudioNspeaker, 0}, ALC882_BEEP},
|
|
{{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC},
|
|
/* AZ_MIXER_SPDIF(AZ_CLASS_PLAYBACK, 0x06),*/
|
|
};
|
|
|
|
static int
|
|
alc882_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(alc882_mixer_items);
|
|
this->mixers = malloc(sizeof(alc882_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, alc882_mixer_items, sizeof(alc882_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
|
|
mc.un.ord = 0; /* [0] 0x0c */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
|
|
generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 1; /* [1] 0x0d */
|
|
generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 2; /* [2] 0x0e */
|
|
generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 2; /* [3] 0x0fb */
|
|
generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
|
|
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
|
|
/* XXX: inamp for 18/19/1a */
|
|
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x24, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc);
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc);
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc882_init_dacgroup(codec_t *this)
|
|
{
|
|
#if 0 /* makes no sense to support for 0x25 node */
|
|
static const convgroupset_t dacs = {
|
|
-1, 3,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}, /* digital */
|
|
{1, {0x25}}}}; /* another analog */
|
|
#else
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
#endif
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc882_set_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
mixer_ctrl_t mc2;
|
|
uint32_t mask, bit;
|
|
int i, err;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
if (mc->type != m->devinfo.type)
|
|
return EINVAL;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0;
|
|
if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
|
|
&& m->target == -1) {
|
|
DPRINTF(("%s: hook for record.*.source\n", __func__));
|
|
mc2.dev = -1;
|
|
mc2.type = AUDIO_MIXER_ENUM;
|
|
bit = 1;
|
|
mask = mc->un.mask & ALC882_MASK;
|
|
for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
|
|
mc2.un.ord = (mask & bit) ? 0 : 1;
|
|
err = generic_mixer_set(this, m->nid,
|
|
MI_TARGET_INAMP(i), &mc2);
|
|
if (err)
|
|
return err;
|
|
bit = bit << 1;
|
|
}
|
|
return 0;
|
|
}
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
static int
|
|
alc882_get_port(codec_t *this, mixer_ctrl_t *mc)
|
|
{
|
|
const mixer_item_t *m;
|
|
uint32_t mask, bit, result;
|
|
int i, err;
|
|
|
|
if (mc->dev >= this->nmixers)
|
|
return ENXIO;
|
|
m = &this->mixers[mc->dev];
|
|
mc->type = m->devinfo.type;
|
|
if (mc->type == AUDIO_MIXER_CLASS)
|
|
return 0;
|
|
if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24)
|
|
&& m->target == -1) {
|
|
DPRINTF(("%s: hook for record.*.source\n", __func__));
|
|
mask = 0;
|
|
bit = 1;
|
|
for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) {
|
|
err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
|
|
CORB_GAGM_INPUT | CORB_GAGM_LEFT |
|
|
i, &result);
|
|
if (err)
|
|
return err;
|
|
if ((result & CORB_GAGM_MUTE) == 0)
|
|
mask |= bit;
|
|
bit = bit << 1;
|
|
}
|
|
mc->un.mask = mask & ALC882_MASK;
|
|
return 0;
|
|
}
|
|
return generic_mixer_get(this, m->nid, m->target, mc);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC883
|
|
* ALC882 without adc07 and mix24.
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc883_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
/* don't support for 0x25 dac */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x08, 0x09}}, /* analog 4ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static const mixer_item_t alc883_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
/* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
|
|
{{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)},
|
|
{{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)},
|
|
{{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)},
|
|
{{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
|
|
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNsurround".dac.mut", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround".mixer.m", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
|
|
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
|
|
|
|
{{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
|
|
{{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
|
|
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
|
|
0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
|
|
{{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
|
|
{{AudioNspeaker, 0}, ALC882_BEEP},
|
|
{{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1},
|
|
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD,
|
|
0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2},
|
|
{{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD},
|
|
{{AudioNspeaker, 0}, ALC882_BEEP},
|
|
{{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1},
|
|
|
|
{{0, {"usingdac", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
{{0, {"usingadc", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_ADC},
|
|
};
|
|
|
|
int
|
|
alc883_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(alc883_mixer_items);
|
|
this->mixers = malloc(sizeof(alc883_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, alc883_mixer_items, sizeof(alc883_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc);
|
|
mc.un.ord = 0; /* [0] 0x0c */
|
|
generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc);
|
|
generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 1; /* [1] 0x0d */
|
|
generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 2; /* [2] 0x0e */
|
|
generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 2; /* [3] 0x0fb */
|
|
generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc);
|
|
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc);
|
|
/* XXX: inamp for 18/19/1a */
|
|
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc);
|
|
generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc);
|
|
return 0;
|
|
}
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC885
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc885_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
/* don't support for 0x25 dac */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Realtek ALC888
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
alc888_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x06}}}}; /* digital */
|
|
/* don't support for 0x25 dac */
|
|
/* ALC888S has another SPDIF-out 0x10 */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x08, 0x09}}, /* analog 4ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc888_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
{
|
|
switch (nid) {
|
|
case 0x0c:
|
|
strlcpy(w->name, AudioNmaster, sizeof(w->name));
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
alc888_mixer_init(codec_t *this)
|
|
{
|
|
mixer_item_t *m, *mdac = NULL;
|
|
mixer_devinfo_t *d;
|
|
int err, i;
|
|
|
|
err = generic_mixer_init(this);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Clear mixer indexes, to make generic_mixer_fix_indexes happy */
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
d = &this->mixers[i].devinfo;
|
|
d->index = d->prev = d->next = 0;
|
|
}
|
|
|
|
/* We're looking for front l/r mixer, which we know is nid 0x0c */
|
|
for (i = 0; i < this->nmixers; i++)
|
|
if (this->mixers[i].nid == 0x0c) {
|
|
mdac = &this->mixers[i];
|
|
break;
|
|
}
|
|
if (mdac) {
|
|
/*
|
|
* ALC888 doesn't have a master mixer, so create a fake
|
|
* inputs.dac that mirrors outputs.master
|
|
*/
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
if (err)
|
|
return err;
|
|
|
|
m = &this->mixers[this->nmixers];
|
|
d = &m->devinfo;
|
|
memcpy(m, mdac, sizeof(*m));
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
snprintf(d->label.name, sizeof(d->label.name), AudioNdac);
|
|
this->nmixers++;
|
|
}
|
|
|
|
/* Recreate mixer indexes and defaults after making a mess of things */
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Analog Devices AD1981HD
|
|
* ---------------------------------------------------------------- */
|
|
|
|
#define AD1981HD_THINKPAD 0x201017aa
|
|
|
|
static const mixer_item_t ad1981hd_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {"lineout." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x05, MI_TARGET_CONNLIST},
|
|
{{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x05, MI_TARGET_INAMP(0)},
|
|
{{0, {"lineout.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x05, MI_TARGET_PINDIR},
|
|
{{0, {"lineout.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_PINBOOST},
|
|
{{0, {"lineout.eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_EAPD},
|
|
|
|
{{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x06, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x06, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(63)}}, 0x07, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x08, MI_TARGET_INAMP(0)},
|
|
|
|
{{0, {"linein." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x09, MI_TARGET_CONNLIST},
|
|
{{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP},
|
|
{{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x09, MI_TARGET_OUTAMP},
|
|
{{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {"linein.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x09, MI_TARGET_PINDIR},
|
|
|
|
{{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={6, {{{AudioNdac, 0}, 0}, {{"mixedmic", 0}, 1},
|
|
{{"linein", 0}, 2}, {{AudioNmixerout, 0}, 3},
|
|
{{"lineout", 0}, 4}, {{"mic2", 0}, 5}}}},
|
|
0x0b, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {"beep." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={2, {{{"digitalbeep", 0}, 0}, {{"beep", 0}, 1}}}},
|
|
0x0d, MI_TARGET_CONNLIST},
|
|
{{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_OUTAMP},
|
|
{{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x0d, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x12, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
|
|
{{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={8, {{{"mixedmic", 0}, 0}, {{"linein", 0}, 1},
|
|
{{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3},
|
|
{{AudioNcd, 0}, 4}, {{"lineout", 0}, 5},
|
|
{{"mic2", 0}, 6}, {{AudioNaux, 0}, 7}}}},
|
|
0x15, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x15, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {"mic2." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x18, MI_TARGET_CONNLIST},
|
|
{{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x18, MI_TARGET_OUTAMP},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x18, MI_TARGET_INAMP(0)},
|
|
{{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x18, MI_TARGET_PINDIR},
|
|
|
|
{{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
|
|
{{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP},
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {"mixedmic.mute1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP},
|
|
{{0, {"mixedmic.mute2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x1f, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
};
|
|
|
|
static int
|
|
ad1981hd_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
{
|
|
switch (nid) {
|
|
case 0x05:
|
|
strlcpy(w->name, AudioNline "out", sizeof(w->name));
|
|
break;
|
|
case 0x06:
|
|
strlcpy(w->name, "hp", sizeof(w->name));
|
|
break;
|
|
case 0x07:
|
|
strlcpy(w->name, AudioNmono, sizeof(w->name));
|
|
break;
|
|
case 0x08:
|
|
strlcpy(w->name, AudioNmicrophone, sizeof(w->name));
|
|
break;
|
|
case 0x09:
|
|
strlcpy(w->name, AudioNline "in", sizeof(w->name));
|
|
break;
|
|
case 0x0d:
|
|
strlcpy(w->name, "beep", sizeof(w->name));
|
|
break;
|
|
case 0x17:
|
|
strlcpy(w->name, AudioNaux, sizeof(w->name));
|
|
break;
|
|
case 0x18:
|
|
strlcpy(w->name, AudioNmicrophone "2", sizeof(w->name));
|
|
break;
|
|
case 0x19:
|
|
strlcpy(w->name, AudioNcd, sizeof(w->name));
|
|
break;
|
|
case 0x1d:
|
|
strlcpy(w->name, AudioNcd, sizeof(w->name));
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1981hd_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(ad1981hd_mixer_items);
|
|
this->mixers = malloc(sizeof(ad1981hd_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, ad1981hd_mixer_items, sizeof(ad1981hd_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
if (this->subid == AD1981HD_THINKPAD) {
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1;
|
|
generic_mixer_set(this, 0x09, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x05, MI_TARGET_EAPD, &mc);
|
|
mc.type = AUDIO_MIXER_VALUE;
|
|
mc.un.value.num_channels = 2;
|
|
mc.un.value.level[0] = AUDIO_MAX_GAIN;
|
|
mc.un.value.level[1] = AUDIO_MAX_GAIN;
|
|
generic_mixer_set(this, 0x1a, MI_TARGET_VOLUME, &mc);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Analog Devices AD1983
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t ad1983_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {AudioNspeaker "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x05, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x05, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x06, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x06, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST},
|
|
|
|
{{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(64)}}, 0x07, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={4, {{{AudioNdac, 0}, 0}, {{AudioNmicrophone, 0}, 1},
|
|
{{AudioNline, 0}, 2}, {{AudioNmixerout, 0}, 3}}}},
|
|
0x0b, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {AudioNmicrophone "." AudioNpreamp "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone "." AudioNpreamp, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(4)}}, 0x0c, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1}}}},
|
|
0x0c, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={2, {{{AudioNline, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}},
|
|
0x0d, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
|
|
{{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(16)}}, 0x10, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x11, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x12, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x13, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1},
|
|
{{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3}}}},
|
|
0x14, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(16)}}, 0x14, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
};
|
|
|
|
static int
|
|
ad1983_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(ad1983_mixer_items);
|
|
this->mixers = malloc(sizeof(ad1983_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, ad1983_mixer_items, sizeof(ad1983_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
#define AD198X_EVENT_HP 1
|
|
#define AD198X_EVENT_SPEAKER 2
|
|
|
|
mc.dev = -1; /* no need for generic_mixer_set() */
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* connlist: mixerout */
|
|
generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc);
|
|
generic_mixer_set(this, 0x06, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 3; /* connlist: mixerout */
|
|
generic_mixer_set(this, 0x0b, MI_TARGET_CONNLIST, &mc);
|
|
|
|
/* setup a unsolicited event for the headphones and speaker */
|
|
this->comresp(this, 0x05, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
|
|
this->comresp(this, 0x06, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
|
|
ad1983_unsol_event(this, AD198X_EVENT_SPEAKER);
|
|
ad1983_unsol_event(this, AD198X_EVENT_HP);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1983_unsol_event(codec_t *this, int tag)
|
|
{
|
|
int err;
|
|
uint32_t value;
|
|
mixer_ctrl_t mc;
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
|
|
switch (tag) {
|
|
case AD198X_EVENT_HP:
|
|
err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: headphone has been inserted.\n", __func__));
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc);
|
|
generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
|
|
} else {
|
|
DPRINTF(("%s: headphone has been pulled out.\n", __func__));
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc);
|
|
/* if no speaker unmute internal mono */
|
|
err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (!(value & CORB_PS_PRESENCE))
|
|
generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
|
|
}
|
|
break;
|
|
case AD198X_EVENT_SPEAKER:
|
|
err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: speaker has been inserted.\n", __func__));
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
|
|
} else {
|
|
DPRINTF(("%s: speaker has been pulled out.\n", __func__));
|
|
/* if no headphones unmute internal mono */
|
|
err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (!(value & CORB_PS_PRESENCE)) {
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Analog Devices AD1984
|
|
* ---------------------------------------------------------------- */
|
|
|
|
#define AD1984_THINKPAD 0x20ac17aa
|
|
#define AD1984_DELL_OPTIPLEX_755 0x02111028
|
|
#define AD1984A_DELL_OPTIPLEX_760 0x027f1028
|
|
|
|
static int
|
|
ad1984_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{2, {0x04, 0x03}}, /* analog 4ch */
|
|
{1, {0x02}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 3,
|
|
{{2, {0x08, 0x09}}, /* analog 4ch */
|
|
{1, {0x06}}, /* digital */
|
|
{1, {0x05}}}}; /* digital */
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1984_mixer_init(codec_t *this)
|
|
{
|
|
int err;
|
|
|
|
err = generic_mixer_autoinit(this);
|
|
if (err)
|
|
return err;
|
|
|
|
if (this->subid == AD1984_DELL_OPTIPLEX_755 ||
|
|
this->subid == AD1984A_DELL_OPTIPLEX_760) {
|
|
/* setup a unsolicited event for the headphones and speaker */
|
|
this->comresp(this, 0x12, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
|
|
this->comresp(this, 0x11, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
|
|
ad1984_unsol_event(this, AD198X_EVENT_SPEAKER);
|
|
ad1984_unsol_event(this, AD198X_EVENT_HP);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1984_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
{
|
|
switch (nid) {
|
|
case 0x07:
|
|
strlcpy(w->name, "hp", sizeof(w->name));
|
|
break;
|
|
case 0x0a:
|
|
strlcpy(w->name, "spkr", sizeof(w->name));
|
|
break;
|
|
case 0x0b:
|
|
strlcpy(w->name, AudioNaux, sizeof(w->name));
|
|
break;
|
|
case 0x0c:
|
|
strlcpy(w->name, "adc08", sizeof(w->name));
|
|
break;
|
|
case 0x0d:
|
|
strlcpy(w->name, "adc09", sizeof(w->name));
|
|
break;
|
|
case 0x0e:
|
|
strlcpy(w->name, AudioNmono "sel", sizeof(w->name));
|
|
break;
|
|
case 0x0f:
|
|
strlcpy(w->name, AudioNaux "sel", sizeof(w->name));
|
|
break;
|
|
case 0x10:
|
|
strlcpy(w->name, "beep", sizeof(w->name));
|
|
break;
|
|
case 0x1e:
|
|
strlcpy(w->name, AudioNmono, sizeof(w->name));
|
|
break;
|
|
case 0x22:
|
|
strlcpy(w->name, "hp" "sel", sizeof(w->name));
|
|
break;
|
|
case 0x23:
|
|
strlcpy(w->name, "dock" "sel", sizeof(w->name));
|
|
break;
|
|
case 0x24:
|
|
strlcpy(w->name, "dock", sizeof(w->name));
|
|
break;
|
|
case 0x25:
|
|
strlcpy(w->name, "dock.pre", sizeof(w->name));
|
|
break;
|
|
default:
|
|
return generic_mixer_init_widget(this, w, nid);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1984_unsol_event(codec_t *this, int tag)
|
|
{
|
|
int err;
|
|
uint32_t value;
|
|
mixer_ctrl_t mc;
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
|
|
switch (tag) {
|
|
case AD198X_EVENT_HP:
|
|
err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: headphone has been inserted.\n", __func__));
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc);
|
|
generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
|
|
} else {
|
|
DPRINTF(("%s: headphone has been pulled out.\n", __func__));
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc);
|
|
/* if no speaker unmute internal mono */
|
|
err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (!(value & CORB_PS_PRESENCE))
|
|
generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
|
|
}
|
|
break;
|
|
case AD198X_EVENT_SPEAKER:
|
|
err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: speaker has been inserted.\n", __func__));
|
|
mc.un.ord = 1; /* mute */
|
|
generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
|
|
} else {
|
|
DPRINTF(("%s: speaker has been pulled out.\n", __func__));
|
|
/* if no headphones unmute internal mono */
|
|
err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (!(value & CORB_PS_PRESENCE)) {
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Analog Devices AD1986A
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t ad1986a_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{"hdaudio", 0}, 0}, {{"recordout", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST},
|
|
|
|
#if 0
|
|
/* fix to mixerout (default) */
|
|
{{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={3, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1},
|
|
{{"clfedac", 0}, 2}}}}, 0x0a, MI_TARGET_CONNLIST},
|
|
#endif
|
|
{{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNheadphone "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP},
|
|
|
|
#if 0
|
|
/* fix to mixerout (default) */
|
|
{{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1}}}},
|
|
0x0b, MI_TARGET_CONNLIST},
|
|
#endif
|
|
{{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNline "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNline "." "eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_EAPD},
|
|
{{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP},
|
|
|
|
#if 0
|
|
/* fix to the surround DAC (default) */
|
|
{{0, {AudioNsurround "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{"surrounddac", 0}, 0}, {{AudioNmixerout, 0}, 1}}}},
|
|
0x0c, MI_TARGET_CONNLIST},
|
|
/* unmute */
|
|
{{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP},
|
|
/* fix to output */
|
|
{{0, {AudioNsurround "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x1c, MI_TARGET_PINDIR},
|
|
/* fix to maximum */
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP},
|
|
#endif
|
|
{{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x04, MI_TARGET_OUTAMP},
|
|
|
|
#if 0
|
|
/* fix to the clfe DAC (default) */
|
|
{{0, {AzaliaNclfe "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{"clfedac", 0}, 0}, {{"mixeroutmono", 0}, 1}}}},
|
|
0x0d, MI_TARGET_CONNLIST},
|
|
/* unmute */
|
|
{{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP},
|
|
/* fix to output */
|
|
{{0, {AzaliaNclfe "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_IO}, 0x1d, MI_TARGET_PINDIR},
|
|
/* fix to maximum */
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP},
|
|
#endif
|
|
{{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe "." "lrswap", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_LRSWAP},
|
|
|
|
{{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}},
|
|
0x0e, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1e, MI_TARGET_OUTAMP},
|
|
|
|
/* Front DAC */
|
|
{{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x03, MI_TARGET_OUTAMP},
|
|
|
|
#if 0
|
|
/* 0x09: 5.1 -> Stereo Downmix */
|
|
{{0, {"downmix" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP},
|
|
#endif
|
|
|
|
#if 0
|
|
/* mic source is mic jack (default) */
|
|
{{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={8, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1},
|
|
{{AzaliaNclfe, 0}, 2}, {{AzaliaNclfe "2", 0}, 3},
|
|
{{"micclfe", 0}, 4}, {{"micline", 0}, 5},
|
|
{{"clfeline", 0}, 6}, {{"miclineclfe", 0}, 7}}}},
|
|
0x0f, MI_TARGET_CONNLIST},
|
|
#endif
|
|
{{0, {AudioNmicrophone "." "gain", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x0f, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP},
|
|
|
|
#if 0
|
|
/* line source is line jack (default) */
|
|
{{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={3, {{{AudioNline, 0}, 0}, {{AudioNsurround, 0}, 1},
|
|
{{AudioNmicrophone, 0}, 2}}}}, 0x10, MI_TARGET_CONNLIST},
|
|
#endif
|
|
{{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x17, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {"phone" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {"phone", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x14, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x15, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x16, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x18, MI_TARGET_OUTAMP},
|
|
|
|
|
|
/* 0x11: inputs.sel11.source=sel0f [ sel0f mix2b ] XXXX
|
|
inputs.sel11.lrswap=off [ off on ] XXXX */
|
|
|
|
{{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={7, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 1},
|
|
{{AudioNaux, 0}, 2}, {{AudioNline, 0}, 3},
|
|
{{AudioNmixerout, 0}, 4}, {{"mixeroutmono", 0}, 5},
|
|
{{"phone", 0}, 6}}}},
|
|
0x12, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNvolume "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x12, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0,
|
|
.un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC},
|
|
};
|
|
|
|
static int
|
|
ad1986a_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(ad1986a_mixer_items);
|
|
this->mixers = malloc(sizeof(ad1986a_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, ad1986a_mixer_items, sizeof(ad1986a_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x1c, MI_TARGET_OUTAMP, &mc);
|
|
mc.un.ord = 1; /* dir: output */
|
|
generic_mixer_set(this, 0x1c, MI_TARGET_PINDIR, &mc);
|
|
mc.type = AUDIO_MIXER_VALUE;
|
|
mc.un.value.num_channels = 2;
|
|
mc.un.value.level[0] = AUDIO_MAX_GAIN;
|
|
mc.un.value.level[1] = AUDIO_MAX_GAIN;
|
|
generic_mixer_set(this, 0x1c, MI_TARGET_VOLUME, &mc);
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 0; /* unmute */
|
|
generic_mixer_set(this, 0x1d, MI_TARGET_OUTAMP, &mc);
|
|
mc.un.ord = 1; /* dir: output */
|
|
generic_mixer_set(this, 0x1d, MI_TARGET_PINDIR, &mc);
|
|
mc.type = AUDIO_MIXER_VALUE;
|
|
mc.un.value.num_channels = 2;
|
|
mc.un.value.level[0] = AUDIO_MAX_GAIN;
|
|
mc.un.value.level[1] = AUDIO_MAX_GAIN;
|
|
generic_mixer_set(this, 0x1d, MI_TARGET_VOLUME, &mc);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ad1986a_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{6, {0x03, 0x04, 0x05}}, /* analog 6ch */
|
|
{1, {0x02}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 1,
|
|
{{1, {0x06}}}}; /* analog 2ch */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Analog Devices AD1988A/AD1988B
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
ad1988_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 3,
|
|
{{4, {0x04, 0x05, 0x06, 0x0a}}, /* analog 8ch */
|
|
{1, {0x02}}, /* digital */
|
|
{1, {0x03}}}}; /* another analog */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x08, 0x09, 0x0f}}, /* analog 6ch */
|
|
{1, {0x07}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* CMedia CMI9880
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t cmi9880_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
|
|
{{0, {AzaliaNdigital"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
|
|
|
|
{{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x08, MI_TARGET_INAMP(0)},
|
|
{{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6},
|
|
{{"line1", 0}, 7}, {{"line2", 0}, 8}}}},
|
|
0x08, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x09, MI_TARGET_INAMP(0)},
|
|
{{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6},
|
|
{{"line1", 0}, 7}, {{"line2", 0}, 8}}}},
|
|
0x09, MI_TARGET_CONNLIST},
|
|
|
|
{{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, ENUM_OFFON}, 0x23, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x23, MI_TARGET_OUTAMP}
|
|
};
|
|
|
|
static int
|
|
cmi9880_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
|
|
this->nmixers = __arraycount(cmi9880_mixer_items);
|
|
this->mixers = malloc(sizeof(cmi9880_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, cmi9880_mixer_items, sizeof(cmi9880_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1;
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 5; /* record.front.source=mic */
|
|
generic_mixer_set(this, 0x08, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 7; /* record.surround.source=line1 */
|
|
generic_mixer_set(this, 0x09, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x0b, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x0c, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc);
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc);
|
|
mc.un.ord = 0; /* front DAC -> headphones */
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_CONNLIST, &mc);
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic */
|
|
generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* SPDIF-in */
|
|
generic_mixer_set(this, 0x1f, MI_TARGET_PINDIR, &mc); /* line1 */
|
|
generic_mixer_set(this, 0x20, MI_TARGET_PINDIR, &mc); /* line2 */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
cmi9880_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 2,
|
|
{{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */
|
|
{1, {0x07}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x08, 0x09}}, /* analog 4ch */
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Sigmatel STAC9221 and STAC9221D
|
|
* ---------------------------------------------------------------- */
|
|
|
|
#define STAC9221_MAC 0x76808384
|
|
|
|
static int
|
|
stac9221_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 3,
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
{1, {0x08}}, /* digital */
|
|
{1, {0x1a}}}}; /* another digital? */
|
|
static const convgroupset_t adcs = {
|
|
-1, 2,
|
|
{{2, {0x06, 0x07}}, /* analog 4ch */
|
|
{1, {0x09}}}}; /* digital */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
stac9221_mixer_init(codec_t *this)
|
|
{
|
|
int err;
|
|
|
|
err = generic_mixer_init(this);
|
|
if (err)
|
|
return err;
|
|
if (this->subid == STAC9221_MAC) {
|
|
stac9221_gpio_unmute(this, 0);
|
|
stac9221_gpio_unmute(this, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
stac9221_gpio_unmute(codec_t *this, int pin)
|
|
{
|
|
uint32_t data, mask, dir;
|
|
|
|
this->comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data);
|
|
this->comresp(this, this->audiofunc,
|
|
CORB_GET_GPIO_ENABLE_MASK, 0, &mask);
|
|
this->comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir);
|
|
data &= ~(1 << pin);
|
|
mask |= 1 << pin;
|
|
dir |= 1 << pin;
|
|
this->comresp(this, this->audiofunc, 0x7e7, 0, NULL);
|
|
this->comresp(this, this->audiofunc,
|
|
CORB_SET_GPIO_ENABLE_MASK, mask, NULL);
|
|
this->comresp(this, this->audiofunc,
|
|
CORB_SET_GPIO_DIRECTION, dir, NULL);
|
|
DELAY(1000);
|
|
this->comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Sigmatel STAC9200 and STAC9200D
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static const mixer_item_t stac9200_mixer_items[] = {
|
|
AZ_MIXER_CLASSES,
|
|
|
|
{{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={3, {{{AudioNdac, 0}, 0}, {{AzaliaNdigital"-in", 0}, 1},
|
|
{{"selector", 0}, 2}}}}, 0x07, MI_TARGET_CONNLIST},
|
|
{{0, {AzaliaNdigital"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{"selector", 0}, 1}}}},
|
|
0x09, MI_TARGET_CONNLIST}, /* AudioNdac is not accurate name */
|
|
{{0, {"selector."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x0a, MI_TARGET_OUTAMP},
|
|
{{0, {"selector", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x0a, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_OUTAMP},
|
|
{{0, {"selector."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
|
0, 0, .un.e={3, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{AudioNcd, 0}, 4}}}},
|
|
0x0c, MI_TARGET_CONNLIST},
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNspeaker".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_PINBOOST},
|
|
{{0, {AudioNmono"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP},
|
|
{{0, {"beep."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT,
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(3)}}, 0x14, MI_TARGET_OUTAMP},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK,
|
|
0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}},
|
|
0, MI_TARGET_DAC},
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}},
|
|
0, MI_TARGET_ADC},
|
|
};
|
|
|
|
static int
|
|
stac9200_mixer_init(codec_t *this)
|
|
{
|
|
mixer_ctrl_t mc;
|
|
uint32_t value;
|
|
|
|
this->nmixers = __arraycount(stac9200_mixer_items);
|
|
this->mixers = malloc(sizeof(stac9200_mixer_items), M_DEVBUF, M_NOWAIT);
|
|
if (this->mixers == NULL) {
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
|
return ENOMEM;
|
|
}
|
|
memcpy(this->mixers, stac9200_mixer_items, sizeof(stac9200_mixer_items));
|
|
generic_mixer_fix_indexes(this);
|
|
generic_mixer_default(this);
|
|
|
|
mc.dev = -1; /* no need for generic_mixer_set() */
|
|
mc.type = AUDIO_MIXER_ENUM;
|
|
mc.un.ord = 1; /* pindir: output */
|
|
generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* headphones */
|
|
generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc); /* speaker */
|
|
mc.un.ord = 0; /* pindir: input */
|
|
generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* mic2 */
|
|
generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic1 */
|
|
mc.type = AUDIO_MIXER_VALUE;
|
|
mc.un.value.num_channels = 2;
|
|
mc.un.value.level[0] = generic_mixer_max(this, 0x0c, MI_TARGET_OUTAMP);
|
|
mc.un.value.level[1] = mc.un.value.level[0];
|
|
generic_mixer_set(this, 0x0c, MI_TARGET_OUTAMP, &mc);
|
|
|
|
#define STAC9200_DELL_INSPIRON6400_ID 0x01bd1028
|
|
#define STAC9200_DELL_INSPIRON9400_ID 0x01cd1028
|
|
#define STAC9200_DELL_640M_ID 0x01d81028
|
|
#define STAC9200_DELL_LATITUDE_D420_ID 0x01d61028
|
|
#define STAC9200_DELL_LATITUDE_D430_ID 0x02011028
|
|
|
|
#define STAC9200_EVENT_HP 0
|
|
#define STAC9200_NID_HP 0x0d
|
|
#define STAC9200_NID_SPEAKER 0x0e
|
|
|
|
switch (this->subid) {
|
|
case STAC9200_DELL_INSPIRON6400_ID:
|
|
case STAC9200_DELL_INSPIRON9400_ID:
|
|
case STAC9200_DELL_640M_ID:
|
|
case STAC9200_DELL_LATITUDE_D420_ID:
|
|
case STAC9200_DELL_LATITUDE_D430_ID:
|
|
/* Does every DELL model have the same pin configuration?
|
|
* I'm not sure. */
|
|
|
|
/* setup a unsolicited event for the headphones */
|
|
this->comresp(this, STAC9200_NID_HP, CORB_SET_UNSOLICITED_RESPONSE,
|
|
CORB_UNSOL_ENABLE | STAC9200_EVENT_HP, NULL);
|
|
/* If the headphone presents, mute the internal speaker */
|
|
this->comresp(this, STAC9200_NID_HP, CORB_GET_PIN_SENSE, 0, &value);
|
|
if (value & CORB_PS_PRESENCE) {
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
|
|
} else {
|
|
generic_mixer_pinctrl(this,
|
|
STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
stac9200_unsol_event(codec_t *this, int tag)
|
|
{
|
|
int err;
|
|
uint32_t value;
|
|
|
|
switch (tag) {
|
|
case STAC9200_EVENT_HP:
|
|
err = this->comresp(this, STAC9200_NID_HP,
|
|
CORB_GET_PIN_SENSE, 0, &value);
|
|
if (err)
|
|
break;
|
|
if (value & CORB_PS_PRESENCE) {
|
|
DPRINTF(("%s: headphone has been inserted.\n", __func__));
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
|
|
} else {
|
|
DPRINTF(("%s: headphone has been pulled out.\n", __func__));
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
|
|
}
|
|
break;
|
|
default:
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ATI HDMI
|
|
* ---------------------------------------------------------------- */
|
|
|
|
static int
|
|
atihdmi_init_dacgroup(codec_t *this)
|
|
{
|
|
static const convgroupset_t dacs = {
|
|
-1, 1,
|
|
{{1, {0x02}}}}; /* digital */
|
|
static const convgroupset_t adcs = {
|
|
-1, 0,
|
|
{{0, {0x00}}}}; /* no recording */
|
|
|
|
this->dacs = dacs;
|
|
this->adcs = adcs;
|
|
return 0;
|
|
}
|
|
|