2010-05-03 08:36:43 +04:00
|
|
|
/* $NetBSD: azalia_codec.c,v 1.78 2010/05/03 04:36:43 dholland Exp $ */
|
2005-09-28 17:06:49 +04:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* 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>
|
2010-05-03 08:36:43 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.78 2010/05/03 04:36:43 dholland Exp $");
|
2005-09-28 17:06:49 +04:00
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/malloc.h>
|
2005-09-28 18:26:19 +04:00
|
|
|
#include <sys/null.h>
|
2005-09-29 08:14:03 +04:00
|
|
|
#include <sys/systm.h>
|
2005-09-28 17:06:49 +04:00
|
|
|
#include <dev/pci/azalia.h>
|
|
|
|
|
2006-06-11 11:52:00 +04:00
|
|
|
#ifdef MAX_VOLUME_255
|
|
|
|
# define MIXER_DELTA(n) (AUDIO_MAX_GAIN / (n))
|
|
|
|
#else
|
|
|
|
# define MIXER_DELTA(n) (1)
|
|
|
|
#endif
|
2006-06-25 17:41:58 +04:00
|
|
|
#define AZ_CLASS_INPUT 0
|
|
|
|
#define AZ_CLASS_OUTPUT 1
|
|
|
|
#define AZ_CLASS_RECORD 2
|
|
|
|
#define AZ_CLASS_PLAYBACK 3
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
#define AZ_CLASS_MIXER 4
|
2006-06-25 17:41:58 +04:00
|
|
|
#define AzaliaCplayback "playback"
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
#define AzaliaCmixer "mix"
|
2006-06-11 19:16:41 +04:00
|
|
|
#define AzaliaNfront "front"
|
|
|
|
#define AzaliaNclfe "clfe"
|
|
|
|
#define AzaliaNside "side"
|
2006-06-25 17:41:58 +04:00
|
|
|
#define AzaliaNdigital "spdif"
|
2006-09-04 02:17:35 +04:00
|
|
|
#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 }
|
2006-06-25 17:41:58 +04:00
|
|
|
#define AZ_MIXER_CLASSES \
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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, \
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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, }
|
2006-06-25 17:41:58 +04:00
|
|
|
#define AZ_MIXER_SPDIF(cl, nid) \
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}}, \
|
2006-06-25 17:41:58 +04:00
|
|
|
nid, MI_TARGET_SPDIF}, \
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNdigital".cc", 0}, AUDIO_MIXER_VALUE, cl, 0, 0, \
|
|
|
|
.un.v={{"", 0}, 1, 1}}, nid, MI_TARGET_SPDIF_CC}
|
2005-09-28 18:26:19 +04:00
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
|
|
|
|
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 *);
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
static int generic_mixer_autoinit(codec_t *);
|
|
|
|
static int generic_mixer_init_widget(const codec_t *, widget_t *, nid_t);
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
static int generic_mixer_fix_indexes(codec_t *);
|
|
|
|
static int generic_mixer_default(codec_t *);
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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 *);
|
2006-06-07 19:23:59 +04:00
|
|
|
static int generic_mixer_delete(codec_t *);
|
2008-01-04 20:00:05 +03:00
|
|
|
static void generic_mixer_cat_names
|
|
|
|
(char *, size_t, const char *, const char *, const char *);
|
2006-06-07 19:23:59 +04:00
|
|
|
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 *);
|
2007-05-08 09:03:50 +04:00
|
|
|
static int generic_mixer_pinctrl(codec_t *, nid_t, uint32_t);
|
2006-06-07 19:23:59 +04:00
|
|
|
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);
|
2006-06-11 11:52:00 +04:00
|
|
|
static uint32_t generic_mixer_max(const codec_t *, nid_t, int);
|
2007-02-22 01:59:35 +03:00
|
|
|
static bool generic_mixer_validate_value
|
2006-06-07 19:23:59 +04:00
|
|
|
(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 *);
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
static int alc260_init_dacgroup(codec_t *);
|
2006-06-24 06:20:54 +04:00
|
|
|
static int alc260_mixer_init(codec_t *);
|
2006-06-07 19:23:59 +04:00
|
|
|
static int alc260_set_port(codec_t *, mixer_ctrl_t *);
|
2006-06-25 11:36:33 +04:00
|
|
|
static int alc260_get_port(codec_t *, mixer_ctrl_t *);
|
|
|
|
static int alc260_unsol_event(codec_t *, int);
|
2007-12-09 23:27:42 +03:00
|
|
|
static int alc262_init_widget(const codec_t *, widget_t *, nid_t);
|
2009-02-28 20:12:13 +03:00
|
|
|
static int alc268_init_dacgroup(codec_t *);
|
2008-03-31 04:18:17 +04:00
|
|
|
static int alc662_init_dacgroup(codec_t *);
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
static int alc861_init_dacgroup(codec_t *);
|
|
|
|
static int alc861vdgr_init_dacgroup(codec_t *);
|
2005-09-28 18:26:19 +04:00
|
|
|
static int alc880_init_dacgroup(codec_t *);
|
2006-06-26 15:06:01 +04:00
|
|
|
static int alc880_mixer_init(codec_t *);
|
2005-09-28 18:26:19 +04:00
|
|
|
static int alc882_init_dacgroup(codec_t *);
|
2006-06-11 19:16:41 +04:00
|
|
|
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 *);
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
static int alc883_init_dacgroup(codec_t *);
|
2007-09-22 09:55:59 +04:00
|
|
|
static int alc883_mixer_init(codec_t *);
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
static int alc885_init_dacgroup(codec_t *);
|
|
|
|
static int alc888_init_dacgroup(codec_t *);
|
2008-02-22 05:59:31 +03:00
|
|
|
static int alc888_init_widget(const codec_t *, widget_t *, nid_t);
|
|
|
|
static int alc888_mixer_init(codec_t *);
|
2006-01-16 17:28:38 +03:00
|
|
|
static int ad1981hd_init_widget(const codec_t *, widget_t *, nid_t);
|
2006-07-23 20:05:21 +04:00
|
|
|
static int ad1981hd_mixer_init(codec_t *);
|
2007-11-03 23:55:58 +03:00
|
|
|
static int ad1983_mixer_init(codec_t *);
|
|
|
|
static int ad1983_unsol_event(codec_t *, int);
|
2008-01-31 01:09:00 +03:00
|
|
|
static int ad1984_init_dacgroup(codec_t *);
|
2007-09-13 07:54:51 +04:00
|
|
|
static int ad1984_init_widget(const codec_t *, widget_t *, nid_t);
|
|
|
|
static int ad1984_mixer_init(codec_t *);
|
2008-01-31 21:58:57 +03:00
|
|
|
static int ad1984_unsol_event(codec_t *, int);
|
2008-01-03 20:39:03 +03:00
|
|
|
static int ad1986a_init_dacgroup(codec_t *);
|
|
|
|
static int ad1986a_mixer_init(codec_t *);
|
2007-03-11 16:34:40 +03:00
|
|
|
static int ad1988_init_dacgroup(codec_t *);
|
2006-06-24 04:33:44 +04:00
|
|
|
static int cmi9880_init_dacgroup(codec_t *);
|
2006-06-24 06:20:54 +04:00
|
|
|
static int cmi9880_mixer_init(codec_t *);
|
2005-09-28 18:26:19 +04:00
|
|
|
static int stac9221_init_dacgroup(codec_t *);
|
2007-01-06 21:35:35 +03:00
|
|
|
static int stac9221_mixer_init(codec_t *);
|
2007-03-11 16:31:36 +03:00
|
|
|
static int stac9221_gpio_unmute(codec_t *, int);
|
2007-05-08 09:03:50 +04:00
|
|
|
static int stac9200_mixer_init(codec_t *);
|
|
|
|
static int stac9200_unsol_event(codec_t *, int);
|
2008-01-03 20:39:03 +03:00
|
|
|
static int atihdmi_init_dacgroup(codec_t *);
|
2005-09-28 18:26:19 +04:00
|
|
|
|
2005-09-28 17:06:49 +04:00
|
|
|
|
|
|
|
int
|
2006-06-07 19:23:59 +04:00
|
|
|
azalia_codec_init_vtbl(codec_t *this)
|
2005-09-28 18:26:19 +04:00
|
|
|
{
|
2006-06-25 11:36:33 +04:00
|
|
|
size_t extra_size;
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
/**
|
|
|
|
* We can refer this->vid and this->subid.
|
|
|
|
*/
|
|
|
|
DPRINTF(("%s: vid=%08x subid=%08x\n", __func__, this->vid, this->subid));
|
2006-06-25 11:36:33 +04:00
|
|
|
extra_size = 0;
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2006-06-25 11:36:33 +04:00
|
|
|
this->unsol_event = NULL;
|
2006-06-07 19:23:59 +04:00
|
|
|
switch (this->vid) {
|
2008-01-03 20:39:03 +03:00
|
|
|
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;
|
2005-09-28 18:26:19 +04:00
|
|
|
case 0x10ec0260:
|
|
|
|
this->name = "Realtek ALC260";
|
2006-06-07 19:23:59 +04:00
|
|
|
this->mixer_init = alc260_mixer_init;
|
2005-09-28 18:26:19 +04:00
|
|
|
this->init_dacgroup = alc260_init_dacgroup;
|
2006-06-07 19:23:59 +04:00
|
|
|
this->set_port = alc260_set_port;
|
2006-06-25 11:36:33 +04:00
|
|
|
this->unsol_event = alc260_unsol_event;
|
|
|
|
extra_size = 1;
|
2005-09-28 18:26:19 +04:00
|
|
|
break;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
case 0x10ec0262:
|
|
|
|
this->name = "Realtek ALC262";
|
2007-12-09 23:27:42 +03:00
|
|
|
this->init_widget = alc262_init_widget;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
break;
|
|
|
|
case 0x10ec0268:
|
|
|
|
this->name = "Realtek ALC268";
|
2009-02-28 20:12:13 +03:00
|
|
|
this->init_dacgroup = alc268_init_dacgroup;
|
|
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
|
|
this->init_widget = generic_mixer_init_widget;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
break;
|
2008-11-12 16:33:10 +03:00
|
|
|
case 0x10ec0269:
|
|
|
|
this->name = "Realtek ALC269";
|
|
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
|
|
this->init_widget = generic_mixer_init_widget;
|
|
|
|
break;
|
2008-03-31 04:18:17 +04:00
|
|
|
case 0x10ec0662:
|
|
|
|
this->name = "Realtek ALC662-GR";
|
|
|
|
this->init_dacgroup = alc662_init_dacgroup;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
|
|
this->init_widget = generic_mixer_init_widget;
|
2008-03-31 04:18:17 +04:00
|
|
|
break;
|
2009-04-07 18:47:53 +04:00
|
|
|
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;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
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;
|
2005-09-28 18:26:19 +04:00
|
|
|
case 0x10ec0880:
|
|
|
|
this->name = "Realtek ALC880";
|
|
|
|
this->init_dacgroup = alc880_init_dacgroup;
|
2006-06-26 15:06:01 +04:00
|
|
|
this->mixer_init = alc880_mixer_init;
|
2005-09-28 18:26:19 +04:00
|
|
|
break;
|
|
|
|
case 0x10ec0882:
|
|
|
|
this->name = "Realtek ALC882";
|
|
|
|
this->init_dacgroup = alc882_init_dacgroup;
|
2006-06-11 19:16:41 +04:00
|
|
|
this->mixer_init = alc882_mixer_init;
|
|
|
|
this->get_port = alc882_get_port;
|
|
|
|
this->set_port = alc882_set_port;
|
2005-09-28 18:26:19 +04:00
|
|
|
break;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
case 0x10ec0883:
|
2007-09-22 09:55:59 +04:00
|
|
|
/* ftp://209.216.61.149/pc/audio/ALC883_DataSheet_1.3.pdf */
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
this->name = "Realtek ALC883";
|
|
|
|
this->init_dacgroup = alc883_init_dacgroup;
|
2007-09-22 09:55:59 +04:00
|
|
|
this->mixer_init = alc883_mixer_init;
|
|
|
|
this->get_port = alc882_get_port;
|
|
|
|
this->set_port = alc882_set_port;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
break;
|
|
|
|
case 0x10ec0885:
|
|
|
|
this->name = "Realtek ALC885";
|
|
|
|
this->init_dacgroup = alc885_init_dacgroup;
|
2009-02-23 05:34:57 +03:00
|
|
|
this->mixer_init = generic_mixer_autoinit;
|
|
|
|
this->init_widget = generic_mixer_init_widget;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
break;
|
|
|
|
case 0x10ec0888:
|
|
|
|
this->name = "Realtek ALC888";
|
|
|
|
this->init_dacgroup = alc888_init_dacgroup;
|
2008-02-22 05:59:31 +03:00
|
|
|
this->init_widget = alc888_init_widget;
|
|
|
|
this->mixer_init = alc888_mixer_init;
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
break;
|
2006-01-16 17:28:38 +03:00
|
|
|
case 0x11d41981:
|
|
|
|
/* http://www.analog.com/en/prod/0,2877,AD1981HD,00.html */
|
|
|
|
this->name = "Analog Devices AD1981HD";
|
|
|
|
this->init_widget = ad1981hd_init_widget;
|
2006-07-23 20:05:21 +04:00
|
|
|
this->mixer_init = ad1981hd_mixer_init;
|
2006-01-16 17:28:38 +03:00
|
|
|
break;
|
2006-06-11 11:52:00 +04:00
|
|
|
case 0x11d41983:
|
|
|
|
/* http://www.analog.com/en/prod/0,2877,AD1983,00.html */
|
|
|
|
this->name = "Analog Devices AD1983";
|
2007-11-03 23:55:58 +03:00
|
|
|
this->mixer_init = ad1983_mixer_init;
|
|
|
|
this->unsol_event = ad1983_unsol_event;
|
2006-06-11 11:52:00 +04:00
|
|
|
break;
|
2007-09-13 07:54:51 +04:00
|
|
|
case 0x11d41984:
|
|
|
|
/* http://www.analog.com/en/prod/0,2877,AD1984,00.html */
|
|
|
|
this->name = "Analog Devices AD1984";
|
2008-01-31 01:09:00 +03:00
|
|
|
this->init_dacgroup = ad1984_init_dacgroup;
|
2007-09-13 07:54:51 +04:00
|
|
|
this->init_widget = ad1984_init_widget;
|
|
|
|
this->mixer_init = ad1984_mixer_init;
|
2008-01-31 21:58:57 +03:00
|
|
|
this->unsol_event = ad1984_unsol_event;
|
2007-09-13 07:54:51 +04:00
|
|
|
break;
|
2009-01-27 11:23:00 +03:00
|
|
|
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;
|
2008-01-03 20:39:03 +03:00
|
|
|
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;
|
2007-03-11 16:34:40 +03:00
|
|
|
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;
|
2006-06-24 04:33:44 +04:00
|
|
|
case 0x434d4980:
|
|
|
|
this->name = "CMedia CMI9880";
|
|
|
|
this->init_dacgroup = cmi9880_init_dacgroup;
|
2006-06-24 06:20:54 +04:00
|
|
|
this->mixer_init = cmi9880_mixer_init;
|
2006-06-24 04:33:44 +04:00
|
|
|
break;
|
2008-01-06 16:33:21 +03:00
|
|
|
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;
|
2005-09-28 18:26:19 +04:00
|
|
|
case 0x83847680:
|
|
|
|
this->name = "Sigmatel STAC9221";
|
|
|
|
this->init_dacgroup = stac9221_init_dacgroup;
|
2007-01-06 21:35:35 +03:00
|
|
|
this->mixer_init = stac9221_mixer_init;
|
2006-01-03 18:43:04 +03:00
|
|
|
break;
|
|
|
|
case 0x83847683:
|
|
|
|
this->name = "Sigmatel STAC9221D";
|
|
|
|
this->init_dacgroup = stac9221_init_dacgroup;
|
|
|
|
break;
|
2006-06-09 20:56:30 +04:00
|
|
|
case 0x83847690:
|
2007-05-08 09:03:50 +04:00
|
|
|
/* 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;
|
2006-06-09 20:56:30 +04:00
|
|
|
break;
|
2005-09-28 18:26:19 +04:00
|
|
|
}
|
2006-06-25 11:36:33 +04:00
|
|
|
if (extra_size > 0) {
|
|
|
|
this->extra = malloc(sizeof(uint32_t) * extra_size,
|
|
|
|
M_DEVBUF, M_ZERO | M_NOWAIT);
|
|
|
|
if (this->extra == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "Not enough memory\n");
|
2006-06-25 11:36:33 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
2005-09-28 18:26:19 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* functions for generic codecs
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int
|
2006-06-07 19:23:59 +04:00
|
|
|
generic_codec_init_dacgroup(codec_t *this)
|
2005-09-28 17:06:49 +04:00
|
|
|
{
|
|
|
|
int i, j, assoc, group;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* grouping DACs
|
|
|
|
* [0] the lowest assoc DACs
|
|
|
|
* [1] the lowest assoc digital outputs
|
|
|
|
* [2] the 2nd assoc DACs
|
|
|
|
* :
|
|
|
|
*/
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.ngroups = 0;
|
2005-09-28 17:06:49 +04:00
|
|
|
for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) {
|
2006-06-07 19:23:59 +04:00
|
|
|
generic_codec_add_dacgroup(this, assoc, 0);
|
|
|
|
generic_codec_add_dacgroup(this, assoc, COP_AWCAP_DIGITAL);
|
2005-09-28 17:06:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* find DACs which do not connect with any pins by default */
|
|
|
|
DPRINTF(("%s: find non-connected DACs\n", __func__));
|
|
|
|
FOR_EACH_WIDGET(this, i) {
|
2007-02-22 01:59:35 +03:00
|
|
|
bool found;
|
2005-09-28 17:06:49 +04:00
|
|
|
|
|
|
|
if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT)
|
|
|
|
continue;
|
|
|
|
found = FALSE;
|
2006-06-11 15:48:39 +04:00
|
|
|
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]) {
|
2005-09-28 17:06:49 +04:00
|
|
|
found = TRUE;
|
2006-06-11 15:48:39 +04:00
|
|
|
group = this->dacs.ngroups;
|
2005-09-28 17:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
continue;
|
2006-06-11 15:48:39 +04:00
|
|
|
if (this->dacs.ngroups >= 32)
|
2005-09-28 17:06:49 +04:00
|
|
|
break;
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.groups[this->dacs.ngroups].nconv = 1;
|
|
|
|
this->dacs.groups[this->dacs.ngroups].conv[0] = i;
|
|
|
|
this->dacs.ngroups++;
|
2005-09-28 17:06:49 +04:00
|
|
|
}
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.cur = 0;
|
2005-09-28 17:06:49 +04:00
|
|
|
|
|
|
|
/* enumerate ADCs */
|
2006-06-11 15:48:39 +04:00
|
|
|
this->adcs.ngroups = 0;
|
2005-09-28 17:06:49 +04:00
|
|
|
FOR_EACH_WIDGET(this, i) {
|
|
|
|
if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT)
|
|
|
|
continue;
|
2006-06-11 15:48:39 +04:00
|
|
|
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)
|
2005-09-28 17:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
2006-06-11 15:48:39 +04:00
|
|
|
this->adcs.cur = 0;
|
2005-09-28 17:06:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-06-07 19:23:59 +04:00
|
|
|
generic_codec_add_dacgroup(codec_t *this, int assoc, uint32_t digital)
|
2005-09-28 17:06:49 +04:00
|
|
|
{
|
|
|
|
int i, j, n, dac, seq;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
for (seq = 0 ; seq < CORB_CD_SEQUENCE_MAX; seq++) {
|
2006-06-07 19:23:59 +04:00
|
|
|
i = generic_codec_find_pin(this, assoc, seq, digital);
|
2005-09-28 17:06:49 +04:00
|
|
|
if (i < 0)
|
|
|
|
continue;
|
2006-06-07 19:23:59 +04:00
|
|
|
dac = generic_codec_find_dac(this, i, 0);
|
2005-09-28 17:06:49 +04:00
|
|
|
if (dac < 0)
|
|
|
|
continue;
|
|
|
|
/* duplication check */
|
|
|
|
for (j = 0; j < n; j++) {
|
2006-06-11 15:48:39 +04:00
|
|
|
if (this->dacs.groups[this->dacs.ngroups].conv[j] == dac)
|
2005-09-28 17:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j < n) /* this group already has <dac> */
|
|
|
|
continue;
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.groups[this->dacs.ngroups].conv[n++] = dac;
|
2005-09-28 17:06:49 +04:00
|
|
|
DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n",
|
2006-06-11 15:48:39 +04:00
|
|
|
__func__, assoc, seq, this->dacs.ngroups, n-1));
|
2005-09-28 17:06:49 +04:00
|
|
|
}
|
|
|
|
if (n <= 0) /* no such DACs */
|
|
|
|
return 0;
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.groups[this->dacs.ngroups].nconv = n;
|
2005-09-28 17:06:49 +04:00
|
|
|
|
|
|
|
/* check if the same combination is already registered */
|
2006-06-11 15:48:39 +04:00
|
|
|
for (i = 0; i < this->dacs.ngroups; i++) {
|
|
|
|
if (n != this->dacs.groups[i].nconv)
|
2005-09-28 17:06:49 +04:00
|
|
|
continue;
|
|
|
|
for (j = 0; j < n; j++) {
|
2006-06-11 15:48:39 +04:00
|
|
|
if (this->dacs.groups[this->dacs.ngroups].conv[j] !=
|
|
|
|
this->dacs.groups[i].conv[j])
|
2005-09-28 17:06:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j >= n) /* matched */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* found no equivalent group */
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.ngroups++;
|
2005-09-28 17:06:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-06-07 19:23:59 +04:00
|
|
|
generic_codec_find_pin(const codec_t *this, int assoc, int seq, uint32_t digital)
|
2005-09-28 17:06:49 +04:00
|
|
|
{
|
|
|
|
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
|
2006-06-07 19:23:59 +04:00
|
|
|
generic_codec_find_dac(const codec_t *this, int index, int depth)
|
2005-09-28 17:06:49 +04:00
|
|
|
{
|
|
|
|
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];
|
2006-06-11 11:52:00 +04:00
|
|
|
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;
|
|
|
|
}
|
2005-09-28 17:06:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < w->nconnections; i++) {
|
|
|
|
j = w->connections[i];
|
2006-06-11 11:52:00 +04:00
|
|
|
if (!VALID_WIDGET_NID(j, this))
|
|
|
|
continue;
|
2006-06-07 19:23:59 +04:00
|
|
|
ret = generic_codec_find_dac(this, j, depth);
|
2005-09-28 17:06:49 +04:00
|
|
|
if (ret >= 0) {
|
|
|
|
DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
|
|
|
|
__func__, w->nid, index));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Generic mixer functions
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2007-03-11 16:41:18 +03:00
|
|
|
#define GMIDPRINTF(x) do {} while (0/*CONSTCOND*/)
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
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) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
|
|
|
|
2006-06-25 17:41:58 +04:00
|
|
|
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;
|
|
|
|
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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;
|
2006-06-07 19:23:59 +04:00
|
|
|
|
|
|
|
#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];
|
|
|
|
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
/* selector */
|
2007-03-18 17:16:00 +03:00
|
|
|
if (w->type != COP_AWTYPE_AUDIO_MIXER &&
|
|
|
|
w->type != COP_AWTYPE_POWER && w->nconnections >= 2) {
|
2006-06-07 19:23:59 +04:00
|
|
|
MIXER_REG_PROLOG;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: selector %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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++) {
|
2008-08-15 15:22:59 +04:00
|
|
|
uint8_t conn;
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
|
|
continue;
|
2008-08-15 15:22:59 +04:00
|
|
|
/* skip unconnected pins */
|
|
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
|
|
conn);
|
|
|
|
if (conn == 1)
|
|
|
|
continue;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: selector %d=%s\n", __func__, j,
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: output mute %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
|
|
"%s.mute", w->name);
|
|
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
|
|
if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: output gain %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
snprintf(d->label.name, sizeof(d->label.name),
|
|
|
|
"%s", w->name);
|
|
|
|
d->type = AUDIO_MIXER_VALUE;
|
|
|
|
if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
2006-06-07 19:23:59 +04:00
|
|
|
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
|
2006-06-11 11:52:00 +04:00
|
|
|
d->un.v.delta =
|
|
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
|
2006-06-07 19:23:59 +04:00
|
|
|
this->nmixers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* input mute */
|
|
|
|
if (w->widgetcap & COP_AWCAP_INAMP &&
|
|
|
|
w->inamp_cap & COP_AMPCAP_MUTE) {
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: input mute %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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 {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
uint8_t conn;
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
for (j = 0; j < w->nconnections; j++) {
|
|
|
|
MIXER_REG_PROLOG;
|
|
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
|
|
continue;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
|
|
|
|
/* skip unconnected pins */
|
|
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
|
|
conn);
|
|
|
|
if (conn == 1)
|
|
|
|
continue;
|
|
|
|
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: input mute %s.%s\n", __func__,
|
2006-06-07 19:23:59 +04:00
|
|
|
w->name, this->w[w->connections[j]].name));
|
2008-01-04 20:00:05 +03:00
|
|
|
generic_mixer_cat_names(
|
|
|
|
d->label.name,
|
|
|
|
sizeof(d->label.name),
|
|
|
|
w->name, this->w[w->connections[j]].name,
|
|
|
|
"mute");
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
else if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
2006-06-07 19:23:59 +04:00
|
|
|
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)) {
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: input gain %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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
|
2006-06-11 11:52:00 +04:00
|
|
|
d->un.v.delta =
|
|
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
|
2006-06-07 19:23:59 +04:00
|
|
|
this->nmixers++;
|
|
|
|
} else {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
uint8_t conn;
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
for (j = 0; j < w->nconnections; j++) {
|
|
|
|
MIXER_REG_PROLOG;
|
|
|
|
if (!VALID_WIDGET_NID(w->connections[j], this))
|
|
|
|
continue;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
/* skip unconnected pins */
|
|
|
|
PIN_STATUS(&this->w[w->connections[j]],
|
|
|
|
conn);
|
|
|
|
if (conn == 1)
|
|
|
|
continue;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: input gain %s.%s\n", __func__,
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
else if (w->type == COP_AWTYPE_AUDIO_MIXER)
|
|
|
|
d->mixer_class = AZ_CLASS_MIXER;
|
2006-06-07 19:23:59 +04:00
|
|
|
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
|
2006-06-11 11:52:00 +04:00
|
|
|
d->un.v.delta =
|
|
|
|
MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: pin dir %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: hpboost %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2006-07-21 18:40:12 +04:00
|
|
|
if (w->type == COP_AWTYPE_PIN_COMPLEX &&
|
|
|
|
w->d.pin.cap & COP_PINCAP_EAPD) {
|
|
|
|
MIXER_REG_PROLOG;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: eapd %s\n", __func__, w->name));
|
2006-07-21 18:40:12 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: balance %s\n", __func__, w->name));
|
2006-07-21 18:40:12 +04:00
|
|
|
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;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: lrswap %s\n", __func__, w->name));
|
2006-07-21 18:40:12 +04:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
/* volume knob */
|
|
|
|
if (w->type == COP_AWTYPE_VOLUME_KNOB &&
|
|
|
|
w->d.volume.cap & COP_VKCAP_DELTA) {
|
|
|
|
MIXER_REG_PROLOG;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: volume knob %s\n", __func__, w->name));
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2006-06-11 11:52:00 +04:00
|
|
|
d->un.v.delta =
|
|
|
|
MIXER_DELTA(COP_VKCAP_NUMSTEPS(w->d.volume.cap));
|
2006-06-07 19:23:59 +04:00
|
|
|
this->nmixers++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-25 17:41:58 +04:00
|
|
|
/* if the codec has multiple DAC groups, create "playback.mode" */
|
2006-06-11 15:48:39 +04:00
|
|
|
if (this->dacs.ngroups > 1) {
|
2006-06-07 19:23:59 +04:00
|
|
|
MIXER_REG_PROLOG;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: create playback.mode\n", __func__));
|
2006-06-25 17:41:58 +04:00
|
|
|
strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
|
2006-06-07 19:23:59 +04:00
|
|
|
d->type = AUDIO_MIXER_ENUM;
|
2006-06-25 17:41:58 +04:00
|
|
|
d->mixer_class = AZ_CLASS_PLAYBACK;
|
2006-06-07 19:23:59 +04:00
|
|
|
m->target = MI_TARGET_DAC;
|
2006-06-11 15:48:39 +04:00
|
|
|
for (i = 0; i < this->dacs.ngroups && i < 32; i++) {
|
2006-06-07 19:23:59 +04:00
|
|
|
d->un.e.member[i].ord = i;
|
2006-06-11 15:48:39 +04:00
|
|
|
for (j = 0; j < this->dacs.groups[i].nconv; j++) {
|
2006-06-07 19:23:59 +04:00
|
|
|
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",
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.groups[i].conv[j]);
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
d->un.e.num_mem = i;
|
|
|
|
this->nmixers++;
|
|
|
|
}
|
|
|
|
|
2006-06-25 17:41:58 +04:00
|
|
|
/* if the codec has multiple ADC groups, create "record.mode" */
|
2006-06-11 15:48:39 +04:00
|
|
|
if (this->adcs.ngroups > 1) {
|
2006-06-07 19:23:59 +04:00
|
|
|
MIXER_REG_PROLOG;
|
2007-03-11 16:41:18 +03:00
|
|
|
GMIDPRINTF(("%s: create record.mode\n", __func__));
|
2006-06-25 17:41:58 +04:00
|
|
|
strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
|
2006-06-07 19:23:59 +04:00
|
|
|
d->type = AUDIO_MIXER_ENUM;
|
|
|
|
d->mixer_class = AZ_CLASS_RECORD;
|
|
|
|
m->target = MI_TARGET_ADC;
|
2006-06-11 15:48:39 +04:00
|
|
|
for (i = 0; i < this->adcs.ngroups && i < 32; i++) {
|
2006-06-07 19:23:59 +04:00
|
|
|
d->un.e.member[i].ord = i;
|
2006-06-11 15:48:39 +04:00
|
|
|
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]);
|
|
|
|
}
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
d->un.e.num_mem = i;
|
|
|
|
this->nmixers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
generic_mixer_fix_indexes(this);
|
|
|
|
generic_mixer_default(this);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-04 20:00:05 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
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) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-07 19:23:59 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-01-06 21:35:35 +03:00
|
|
|
* For bidirectional pins, make the default `output'
|
2006-06-07 19:23:59 +04:00
|
|
|
*/
|
|
|
|
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;
|
2007-01-06 21:35:35 +03:00
|
|
|
mc.un.ord = 1; /* output */
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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];
|
2008-08-24 20:12:03 +04:00
|
|
|
int i, err, mdac, madc, mmaster;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2008-08-24 20:12:03 +04:00
|
|
|
mdac = madc = mmaster = -1;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
for (i = 0; i < this->nmixers; i++) {
|
|
|
|
if (this->mixers[i].devinfo.type != AUDIO_MIXER_VALUE)
|
|
|
|
continue;
|
2008-08-24 20:12:03 +04:00
|
|
|
if (mdac < 0 && this->dacs.ngroups > 0 && cgdac->nconv > 0) {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
if (this->mixers[i].nid == cgdac->conv[0])
|
2008-08-24 20:12:03 +04:00
|
|
|
mdac = mmaster = i;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
}
|
2008-08-24 20:12:03 +04:00
|
|
|
if (madc < 0 && this->adcs.ngroups > 0 && cgadc->nconv > 0) {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
if (this->mixers[i].nid == cgadc->conv[0])
|
2008-08-24 20:12:03 +04:00
|
|
|
madc = i;
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-23 05:34:57 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-24 20:12:03 +04:00
|
|
|
if (mdac >= 0) {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
m = &this->mixers[this->nmixers];
|
|
|
|
d = &m->devinfo;
|
2008-08-24 20:12:03 +04:00
|
|
|
memcpy(m, &this->mixers[mmaster], sizeof(*m));
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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;
|
2008-08-24 20:12:03 +04:00
|
|
|
memcpy(m, &this->mixers[mdac], sizeof(*m));
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
d->mixer_class = AZ_CLASS_INPUT;
|
|
|
|
snprintf(d->label.name, sizeof(d->label.name), AudioNdac);
|
|
|
|
this->nmixers++;
|
|
|
|
}
|
|
|
|
|
2008-08-24 20:12:03 +04:00
|
|
|
if (madc >= 0) {
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
err = generic_mixer_ensure_capacity(this, this->nmixers + 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
m = &this->mixers[this->nmixers];
|
|
|
|
d = &m->devinfo;
|
2008-08-24 20:12:03 +04:00
|
|
|
memcpy(m, &this->mixers[madc], sizeof(*m));
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2006-06-15 20:11:15 +04:00
|
|
|
nid_t n;
|
2006-06-07 19:23:59 +04:00
|
|
|
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));
|
2006-07-19 06:40:18 +04:00
|
|
|
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;
|
|
|
|
}
|
2006-06-15 20:11:15 +04:00
|
|
|
mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
|
2006-06-07 19:23:59 +04:00
|
|
|
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) {
|
2006-06-11 15:48:39 +04:00
|
|
|
mc->un.ord = this->dacs.cur;
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ADC selection */
|
|
|
|
else if (target == MI_TARGET_ADC) {
|
2006-06-11 15:48:39 +04:00
|
|
|
mc->un.ord = this->adcs.cur;
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2006-06-25 17:41:58 +04:00
|
|
|
/* S/PDIF */
|
|
|
|
else if (target == MI_TARGET_SPDIF) {
|
|
|
|
err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
|
|
|
|
0, &result);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2006-06-26 15:06:01 +04:00
|
|
|
mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO);
|
2006-06-25 17:41:58 +04:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
|
2006-07-21 18:40:12 +04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
else {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
|
|
|
|
__func__, target);
|
2006-06-07 19:23:59 +04:00
|
|
|
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) {
|
2007-05-08 09:03:50 +04:00
|
|
|
return generic_mixer_pinctrl(this, nid, CORB_PWC_INPUT);
|
2006-06-07 19:23:59 +04:00
|
|
|
} else {
|
2007-05-08 09:03:50 +04:00
|
|
|
return generic_mixer_pinctrl(
|
|
|
|
this, nid, CORB_PWC_OUTPUT);
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2006-06-11 15:48:39 +04:00
|
|
|
if (mc->un.ord >= this->dacs.ngroups)
|
2006-06-07 19:23:59 +04:00
|
|
|
return EINVAL;
|
2006-06-09 20:56:30 +04:00
|
|
|
return azalia_codec_construct_format(this,
|
2006-06-11 15:48:39 +04:00
|
|
|
mc->un.ord, this->adcs.cur);
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ADC selection */
|
|
|
|
else if (target == MI_TARGET_ADC) {
|
|
|
|
if (this->running)
|
|
|
|
return EBUSY;
|
2006-06-11 15:48:39 +04:00
|
|
|
if (mc->un.ord >= this->adcs.ngroups)
|
2006-06-07 19:23:59 +04:00
|
|
|
return EINVAL;
|
2006-06-09 20:56:30 +04:00
|
|
|
return azalia_codec_construct_format(this,
|
2006-06-11 15:48:39 +04:00
|
|
|
this->dacs.cur, mc->un.ord);
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2006-06-25 17:41:58 +04:00
|
|
|
/* S/PDIF */
|
|
|
|
else if (target == MI_TARGET_SPDIF) {
|
|
|
|
err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
|
|
|
|
0, &result);
|
2006-06-26 15:06:01 +04:00
|
|
|
result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO;
|
2006-06-25 17:41:58 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-07-21 18:40:12 +04:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
else {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "internal error in %s: target=%x\n",
|
|
|
|
__func__, target);
|
2006-06-07 19:23:59 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-08 09:03:50 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
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);
|
2008-01-04 15:18:00 +03:00
|
|
|
dmax = 127;
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
2008-01-04 15:18:00 +03:00
|
|
|
return dv * MIXER_DELTA(dmax);
|
2006-06-07 19:23:59 +04:00
|
|
|
#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);
|
2008-01-04 15:18:00 +03:00
|
|
|
dmax = 127;
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
2008-01-04 15:18:00 +03:00
|
|
|
return uv / MIXER_DELTA(dmax);
|
2006-06-07 19:23:59 +04:00
|
|
|
#else
|
|
|
|
return uv;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-06-11 11:52:00 +04:00
|
|
|
static uint32_t
|
2006-11-16 04:32:37 +03:00
|
|
|
generic_mixer_max(const codec_t *this, nid_t nid,
|
|
|
|
int target)
|
2006-06-07 19:23:59 +04:00
|
|
|
{
|
|
|
|
#ifdef MAX_VOLUME_255
|
2006-06-11 11:52:00 +04:00
|
|
|
return AUDIO_MAX_GAIN;
|
2006-06-07 19:23:59 +04:00
|
|
|
#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);
|
2006-06-11 11:52:00 +04:00
|
|
|
return dmax;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2007-02-22 01:59:35 +03:00
|
|
|
static bool
|
2006-11-16 04:32:37 +03:00
|
|
|
generic_mixer_validate_value(const codec_t *this, nid_t nid,
|
|
|
|
int target, u_char uv)
|
2006-06-11 11:52:00 +04:00
|
|
|
{
|
|
|
|
#ifdef MAX_VOLUME_255
|
|
|
|
return TRUE;
|
|
|
|
#else
|
|
|
|
return uv <= generic_mixer_max(this, nid, target);
|
2006-06-07 19:23:59 +04:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Realtek ALC260
|
2006-06-07 19:23:59 +04:00
|
|
|
*
|
|
|
|
* Fujitsu LOOX T70M/T
|
|
|
|
* Internal Speaker: 0x10
|
|
|
|
* Front Headphone: 0x14
|
|
|
|
* Front mic: 0x12
|
2005-09-28 18:26:19 +04:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2006-06-07 19:23:59 +04:00
|
|
|
#define ALC260_FUJITSU_ID 0x132610cf
|
2006-06-25 11:36:33 +04:00
|
|
|
#define ALC260_EVENT_HP 0
|
|
|
|
#define ALC260_EXTRA_MASTER 0
|
2006-06-07 19:23:59 +04:00
|
|
|
static const mixer_item_t alc260_mixer_items[] = {
|
2006-06-25 17:41:58 +04:00
|
|
|
AZ_MIXER_CLASSES,
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmono".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_IO}, 0x12, MI_TARGET_PINDIR},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_IO}, 0x13, MI_TARGET_PINDIR},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_IO}, 0x14, MI_TARGET_PINDIR},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_IO}, 0x15, MI_TARGET_PINDIR},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(2)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(3)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
|
2006-06-07 19:23:59 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-07 19:23:59 +04:00
|
|
|
0x04, MI_TARGET_CONNLIST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
2006-06-07 19:23:59 +04:00
|
|
|
ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-07 19:23:59 +04:00
|
|
|
0x05, MI_TARGET_CONNLIST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
2006-06-07 19:23:59 +04:00
|
|
|
ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
|
2006-06-07 19:23:59 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-25 17:41:58 +04:00
|
|
|
0, MI_TARGET_ADC},
|
2006-06-07 19:23:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static const mixer_item_t alc260_loox_mixer_items[] = {
|
2006-06-25 17:41:58 +04:00
|
|
|
AZ_MIXER_CLASSES,
|
2006-06-07 19:23:59 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmicrophone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)},
|
2006-06-07 19:23:59 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-07 19:23:59 +04:00
|
|
|
ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-07 19:23:59 +04:00
|
|
|
0x05, MI_TARGET_CONNLIST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0,
|
2006-06-07 19:23:59 +04:00
|
|
|
ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0,
|
|
|
|
.un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)},
|
2006-06-07 19:23:59 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-25 17:41:58 +04:00
|
|
|
0, MI_TARGET_ADC},
|
2006-06-07 19:23:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
alc260_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
const mixer_item_t *mi;
|
|
|
|
mixer_ctrl_t mc;
|
2006-06-25 11:36:33 +04:00
|
|
|
uint32_t value;
|
2006-06-07 19:23:59 +04:00
|
|
|
|
|
|
|
switch (this->subid) {
|
|
|
|
case ALC260_FUJITSU_ID:
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(alc260_loox_mixer_items);
|
2006-06-07 19:23:59 +04:00
|
|
|
mi = alc260_loox_mixer_items;
|
|
|
|
break;
|
|
|
|
default:
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(alc260_mixer_items);
|
2006-06-07 19:23:59 +04:00
|
|
|
mi = alc260_mixer_items;
|
|
|
|
}
|
|
|
|
this->mixers = malloc(sizeof(mixer_item_t) * this->nmixers,
|
2008-01-03 21:00:43 +03:00
|
|
|
M_DEVBUF, M_NOWAIT);
|
2006-06-07 19:23:59 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-07 19:23:59 +04:00
|
|
|
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);
|
2006-06-25 11:36:33 +04:00
|
|
|
/* 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);
|
2007-05-08 09:18:20 +04:00
|
|
|
mc.un.ord = value & CORB_PS_PRESENCE ? 1 : 0;
|
2006-06-25 11:36:33 +04:00
|
|
|
generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc);
|
|
|
|
this->get_port = alc260_get_port;
|
2006-06-07 19:23:59 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
static int
|
|
|
|
alc260_init_dacgroup(codec_t *this)
|
|
|
|
{
|
2006-06-11 15:48:39 +04:00
|
|
|
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;
|
2005-09-28 18:26:19 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-01-03 18:31:15 +03:00
|
|
|
static int
|
2006-06-07 19:23:59 +04:00
|
|
|
alc260_set_port(codec_t *this, mixer_ctrl_t *mc)
|
2006-01-03 18:31:15 +03:00
|
|
|
{
|
2006-06-07 19:23:59 +04:00
|
|
|
const mixer_item_t *m;
|
|
|
|
mixer_ctrl_t mc2;
|
2006-06-25 11:36:33 +04:00
|
|
|
uint32_t value;
|
2006-06-07 19:23:59 +04:00
|
|
|
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;
|
2006-06-25 11:36:33 +04:00
|
|
|
} 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;
|
2007-05-08 09:18:20 +04:00
|
|
|
if (!(value & CORB_PS_PRESENCE)) {
|
2006-06-25 11:36:33 +04:00
|
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
|
|
|
}
|
|
|
|
return 0;
|
2006-01-03 18:31:15 +03:00
|
|
|
}
|
2006-06-07 19:23:59 +04:00
|
|
|
return generic_mixer_set(this, m->nid, m->target, mc);
|
2006-01-03 18:31:15 +03:00
|
|
|
}
|
|
|
|
|
2006-06-25 11:36:33 +04:00
|
|
|
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;
|
2007-05-08 09:18:20 +04:00
|
|
|
if (value & CORB_PS_PRESENCE) {
|
2006-06-25 11:36:33 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
/* ----------------------------------------------------------------
|
2009-02-28 20:12:13 +03:00
|
|
|
* Realtek ALC262
|
2007-12-09 23:27:42 +03:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-02-28 20:12:13 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2008-03-31 04:18:17 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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,
|
PR#38860: azalia record does not work on Realtek ALC662-GR
o Don't add unconnected pins to generated mixer list, should help
make auto-generated mixer lists slightly easier to work with.
o Move COP_AWTYPE_AUDIO_MIXER type knobs to mix.* rather than polluting
the inputs.* namespace and giving a few more characters for the
auto-generated names.
o Introduce an alternate to generic_mixer_init, generic_mixer_autoinit.
generic_mixer_autoinit uses COP_AWTYPE_PIN_COMPLEX "default
configuration" capability flags to determine proper pin widget control
and eapd configuration, as well as creating virtual outputs.master,
inputs.dac, and record.volume mixer controls that mirror the default
dac and adc controls.
o Introduce generic_mixer_init_widget which uses COP_AWTYPE_PIN_COMPLEX
"default configuration" device type, gross location, and geometric
locations to guess friendlier mixer control names.
o On ALC662-GR, the primary ADC NID is 0x09, not 0x08, so correct the
convgroupset in alc662_init_dacgroup.
Audio capture now works on ALC662-GR (as found in the ASUS EeePC 701).
Note that only ALC662-GR uses generic_mixer_autoinit and
generic_mixer_init_widget; other codecs will need to be tested and
converted to address PR#38578: azalia_codec.o is too large.
2008-08-15 03:43:27 +04:00
|
|
|
{{2, {0x09, 0x08}}}}; /* analog 4ch */
|
2008-03-31 04:18:17 +04:00
|
|
|
|
|
|
|
this->dacs = dacs;
|
|
|
|
this->adcs = adcs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Realtek ALC880
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2006-06-26 15:06:01 +04:00
|
|
|
static const mixer_item_t alc880_mixer_items[] = {
|
|
|
|
AZ_MIXER_CLASSES,
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD,
|
|
|
|
0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x09, MI_TARGET_INAMP(0)},
|
2006-06-26 15:06:01 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(3)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(5)},
|
2006-06-26 15:06:01 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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},
|
2006-06-26 15:06:01 +04:00
|
|
|
#if 0 /* The followings are useless. */
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)},
|
|
|
|
#endif
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x19, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x19, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_IO}, 0x19, MI_TARGET_PINDIR},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line1.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line1.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_IO}, 0x1a, MI_TARGET_PINDIR},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"line2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-26 15:06:01 +04:00
|
|
|
0, 0, ENUM_IO}, 0x1b, MI_TARGET_PINDIR},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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},
|
2006-06-26 15:06:01 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
alc880_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
mixer_ctrl_t mc;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(alc880_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(alc880_mixer_items), M_DEVBUF, M_NOWAIT);
|
2006-06-26 15:06:01 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-26 15:06:01 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, alc880_mixer_items, sizeof(alc880_mixer_items));
|
2006-06-26 15:06:01 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
static int
|
|
|
|
alc880_init_dacgroup(codec_t *this)
|
|
|
|
{
|
2006-06-11 15:48:39 +04:00
|
|
|
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;
|
2005-09-28 18:26:19 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Realtek ALC882
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2006-06-11 19:16:41 +04:00
|
|
|
static const mixer_item_t alc882_mixer_items[] = {
|
2006-06-25 17:41:58 +04:00
|
|
|
AZ_MIXER_CLASSES,
|
2006-06-11 19:16:41 +04:00
|
|
|
|
|
|
|
/* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)},
|
2006-06-11 19:16:41 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 19:16:41 +04:00
|
|
|
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
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 19:16:41 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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},
|
2006-06-26 15:06:01 +04:00
|
|
|
/* AZ_MIXER_SPDIF(AZ_CLASS_PLAYBACK, 0x06),*/
|
2006-06-11 19:16:41 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
alc882_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
mixer_ctrl_t mc;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(alc882_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(alc882_mixer_items), M_DEVBUF, M_NOWAIT);
|
2006-06-11 19:16:41 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-11 19:16:41 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, alc882_mixer_items, sizeof(alc882_mixer_items));
|
2006-06-11 19:16:41 +04:00
|
|
|
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);
|
2006-06-26 15:06:01 +04:00
|
|
|
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);
|
2006-06-11 19:16:41 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
static int
|
|
|
|
alc882_init_dacgroup(codec_t *this)
|
|
|
|
{
|
2006-06-11 19:16:41 +04:00
|
|
|
#if 0 /* makes no sense to support for 0x25 node */
|
2006-06-11 15:48:39 +04:00
|
|
|
static const convgroupset_t dacs = {
|
|
|
|
-1, 3,
|
|
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
|
|
{1, {0x06}}, /* digital */
|
|
|
|
{1, {0x25}}}}; /* another analog */
|
2006-06-11 19:16:41 +04:00
|
|
|
#else
|
|
|
|
static const convgroupset_t dacs = {
|
|
|
|
-1, 2,
|
|
|
|
{{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */
|
|
|
|
{1, {0x06}}}}; /* digital */
|
|
|
|
#endif
|
2006-06-11 15:48:39 +04:00
|
|
|
static const convgroupset_t adcs = {
|
|
|
|
-1, 2,
|
|
|
|
{{3, {0x07, 0x08, 0x09}}, /* analog 6ch */
|
|
|
|
{1, {0x0a}}}}; /* digital */
|
|
|
|
|
|
|
|
this->dacs = dacs;
|
|
|
|
this->adcs = adcs;
|
2005-09-29 08:14:03 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-11 19:16:41 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Realtek ALC883
|
2007-09-22 09:55:59 +04:00
|
|
|
* ALC882 without adc07 and mix24.
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-09-22 09:55:59 +04:00
|
|
|
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;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(alc883_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(alc883_mixer_items), M_DEVBUF, M_NOWAIT);
|
2007-09-22 09:55:59 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2007-09-22 09:55:59 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, alc883_mixer_items, sizeof(alc883_mixer_items));
|
2007-09-22 09:55:59 +04:00
|
|
|
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;
|
|
|
|
}
|
* codec names for Realtek ALC262, ALC268, ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
* DAC/ADC group definitions for Realtek ALC861, ALC861-VD-GR,
ALC883, ALC885, and ALC888
2007-03-11 18:42:00 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2008-02-22 05:59:31 +03:00
|
|
|
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)
|
|
|
|
{
|
2010-05-03 08:36:43 +04:00
|
|
|
mixer_item_t *m = NULL;
|
2008-02-22 05:59:31 +03:00
|
|
|
mixer_devinfo_t *d;
|
2010-05-03 08:36:43 +04:00
|
|
|
int err, i, mdac_index = -1;
|
2008-02-22 05:59:31 +03:00
|
|
|
|
|
|
|
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) {
|
2010-05-03 08:36:43 +04:00
|
|
|
mdac_index = i;
|
2008-02-22 05:59:31 +03:00
|
|
|
break;
|
|
|
|
}
|
2010-05-03 08:36:43 +04:00
|
|
|
if (mdac_index >= 0) {
|
2008-02-22 05:59:31 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
2010-05-03 08:36:43 +04:00
|
|
|
memcpy(m, &this->mixers[mdac_index], sizeof(*m));
|
2008-02-22 05:59:31 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-01-16 17:28:38 +03:00
|
|
|
/* ----------------------------------------------------------------
|
2006-05-10 15:17:27 +04:00
|
|
|
* Analog Devices AD1981HD
|
2006-01-16 17:28:38 +03:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2006-07-23 20:05:21 +04:00
|
|
|
#define AD1981HD_THINKPAD 0x201017aa
|
|
|
|
|
2007-03-19 19:27:00 +03:00
|
|
|
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},
|
|
|
|
};
|
|
|
|
|
2006-01-16 17:28:38 +03:00
|
|
|
static int
|
2006-11-16 04:32:37 +03:00
|
|
|
ad1981hd_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
2006-01-16 17:28:38 +03:00
|
|
|
{
|
|
|
|
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:
|
2007-03-19 19:27:00 +03:00
|
|
|
strlcpy(w->name, AudioNcd, sizeof(w->name));
|
2006-01-16 17:28:38 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-23 20:05:21 +04:00
|
|
|
static int
|
|
|
|
ad1981hd_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
mixer_ctrl_t mc;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(ad1981hd_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(ad1981hd_mixer_items), M_DEVBUF, M_NOWAIT);
|
2007-03-19 19:27:00 +03:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2007-03-19 19:27:00 +03:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, ad1981hd_mixer_items, sizeof(ad1981hd_mixer_items));
|
2007-03-19 19:27:00 +03:00
|
|
|
generic_mixer_fix_indexes(this);
|
|
|
|
generic_mixer_default(this);
|
|
|
|
|
2006-07-23 20:05:21 +04:00
|
|
|
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);
|
2008-07-06 04:14:00 +04:00
|
|
|
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);
|
2006-07-23 20:05:21 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-11-03 23:55:58 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(ad1983_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(ad1983_mixer_items), M_DEVBUF, M_NOWAIT);
|
2007-11-03 23:55:58 +03:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2007-11-03 23:55:58 +03:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, ad1983_mixer_items, sizeof(ad1983_mixer_items));
|
2007-11-03 23:55:58 +03:00
|
|
|
generic_mixer_fix_indexes(this);
|
|
|
|
generic_mixer_default(this);
|
|
|
|
|
2008-01-31 22:01:50 +03:00
|
|
|
#define AD198X_EVENT_HP 1
|
|
|
|
#define AD198X_EVENT_SPEAKER 2
|
2007-11-03 23:55:58 +03:00
|
|
|
|
|
|
|
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,
|
2008-01-31 22:01:50 +03:00
|
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
|
2007-11-03 23:55:58 +03:00
|
|
|
this->comresp(this, 0x06, CORB_SET_UNSOLICITED_RESPONSE,
|
2008-01-31 22:01:50 +03:00
|
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
|
|
|
|
ad1983_unsol_event(this, AD198X_EVENT_SPEAKER);
|
|
|
|
ad1983_unsol_event(this, AD198X_EVENT_HP);
|
2007-11-03 23:55:58 +03:00
|
|
|
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) {
|
2008-01-31 22:01:50 +03:00
|
|
|
case AD198X_EVENT_HP:
|
2007-11-03 23:55:58 +03:00
|
|
|
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;
|
2008-01-31 22:01:50 +03:00
|
|
|
case AD198X_EVENT_SPEAKER:
|
2007-11-03 23:55:58 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-09-13 07:54:51 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Analog Devices AD1984
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2008-01-31 21:58:57 +03:00
|
|
|
#define AD1984_THINKPAD 0x20ac17aa
|
|
|
|
#define AD1984_DELL_OPTIPLEX_755 0x02111028
|
2009-01-27 11:23:00 +03:00
|
|
|
#define AD1984A_DELL_OPTIPLEX_760 0x027f1028
|
2007-09-13 07:54:51 +04:00
|
|
|
|
2008-01-31 01:09:00 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-09-13 07:54:51 +04:00
|
|
|
static int
|
|
|
|
ad1984_mixer_init(codec_t *this)
|
|
|
|
{
|
2008-08-15 17:47:25 +04:00
|
|
|
int err;
|
2007-09-13 07:54:51 +04:00
|
|
|
|
2008-08-15 17:47:25 +04:00
|
|
|
err = generic_mixer_autoinit(this);
|
2007-09-13 07:54:51 +04:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2009-01-27 11:23:00 +03:00
|
|
|
if (this->subid == AD1984_DELL_OPTIPLEX_755 ||
|
|
|
|
this->subid == AD1984A_DELL_OPTIPLEX_760) {
|
2008-01-31 21:58:57 +03:00
|
|
|
/* setup a unsolicited event for the headphones and speaker */
|
|
|
|
this->comresp(this, 0x12, CORB_SET_UNSOLICITED_RESPONSE,
|
2008-01-31 22:01:50 +03:00
|
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL);
|
2008-01-31 21:58:57 +03:00
|
|
|
this->comresp(this, 0x11, CORB_SET_UNSOLICITED_RESPONSE,
|
2008-01-31 22:01:50 +03:00
|
|
|
CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL);
|
|
|
|
ad1984_unsol_event(this, AD198X_EVENT_SPEAKER);
|
|
|
|
ad1984_unsol_event(this, AD198X_EVENT_HP);
|
2008-01-31 21:58:57 +03:00
|
|
|
}
|
2007-09-13 07:54:51 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ad1984_init_widget(const codec_t *this, widget_t *w, nid_t nid)
|
|
|
|
{
|
2008-01-31 21:58:57 +03:00
|
|
|
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;
|
2008-08-15 17:47:25 +04:00
|
|
|
default:
|
|
|
|
return generic_mixer_init_widget(this, w, nid);
|
2008-01-31 21:58:57 +03:00
|
|
|
}
|
2008-08-15 17:47:25 +04:00
|
|
|
|
2008-01-31 21:58:57 +03:00
|
|
|
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) {
|
2008-01-31 22:01:50 +03:00
|
|
|
case AD198X_EVENT_HP:
|
2008-01-31 21:58:57 +03:00
|
|
|
err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value);
|
|
|
|
if (err)
|
2007-09-13 07:54:51 +04:00
|
|
|
break;
|
2008-01-31 21:58:57 +03:00
|
|
|
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;
|
2008-01-31 22:01:50 +03:00
|
|
|
case AD198X_EVENT_SPEAKER:
|
2008-01-31 21:58:57 +03:00
|
|
|
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);
|
|
|
|
}
|
2007-09-13 07:54:51 +04:00
|
|
|
}
|
2008-01-31 21:58:57 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
2007-09-13 07:54:51 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-01-03 20:39:03 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* 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;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(ad1986a_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(ad1986a_mixer_items), M_DEVBUF, M_NOWAIT);
|
2008-01-03 20:39:03 +03:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2008-01-03 20:39:03 +03:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, ad1986a_mixer_items, sizeof(ad1986a_mixer_items));
|
2008-01-03 20:39:03 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-03-11 16:34:40 +03:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Analog Devices AD1988A/AD1988B
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int
|
|
|
|
ad1988_init_dacgroup(codec_t *this)
|
|
|
|
{
|
|
|
|
static const convgroupset_t dacs = {
|
|
|
|
-1, 3,
|
2007-03-18 14:49:21 +03:00
|
|
|
{{4, {0x04, 0x05, 0x06, 0x0a}}, /* analog 8ch */
|
2007-03-11 16:34:40 +03:00
|
|
|
{1, {0x02}}, /* digital */
|
2007-03-18 14:49:21 +03:00
|
|
|
{1, {0x03}}}}; /* another analog */
|
2007-03-11 16:34:40 +03:00
|
|
|
static const convgroupset_t adcs = {
|
|
|
|
-1, 2,
|
|
|
|
{{2, {0x08, 0x09, 0x0f}}, /* analog 6ch */
|
|
|
|
{1, {0x07}}}}; /* digital */
|
|
|
|
|
|
|
|
this->dacs = dacs;
|
|
|
|
this->adcs = adcs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-24 04:33:44 +04:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* CMedia CMI9880
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2006-06-24 06:20:54 +04:00
|
|
|
static const mixer_item_t cmi9880_mixer_items[] = {
|
2006-06-25 17:41:58 +04:00
|
|
|
AZ_MIXER_CLASSES,
|
2006-06-24 06:20:54 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNdigital"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-24 06:20:54 +04:00
|
|
|
0x08, MI_TARGET_CONNLIST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-24 06:20:54 +04:00
|
|
|
0x09, MI_TARGET_CONNLIST},
|
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT,
|
2006-06-24 06:20:54 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x23, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT,
|
|
|
|
0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x23, MI_TARGET_OUTAMP}
|
2006-06-24 06:20:54 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmi9880_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
mixer_ctrl_t mc;
|
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(cmi9880_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(cmi9880_mixer_items), M_DEVBUF, M_NOWAIT);
|
2006-06-24 06:20:54 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-24 06:20:54 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, cmi9880_mixer_items, sizeof(cmi9880_mixer_items));
|
2006-06-24 06:20:54 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-06-24 04:33:44 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
/* ----------------------------------------------------------------
|
2006-01-03 18:43:04 +03:00
|
|
|
* Sigmatel STAC9221 and STAC9221D
|
2005-09-28 18:26:19 +04:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2007-01-06 21:35:35 +03:00
|
|
|
#define STAC9221_MAC 0x76808384
|
|
|
|
|
2005-09-28 18:26:19 +04:00
|
|
|
static int
|
|
|
|
stac9221_init_dacgroup(codec_t *this)
|
|
|
|
{
|
2006-06-11 15:48:39 +04:00
|
|
|
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;
|
2006-06-11 11:52:00 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-06 21:35:35 +03:00
|
|
|
static int
|
|
|
|
stac9221_mixer_init(codec_t *this)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = generic_mixer_init(this);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-05-13 07:28:19 +04:00
|
|
|
if (this->subid == STAC9221_MAC) {
|
2007-03-11 16:31:36 +03:00
|
|
|
stac9221_gpio_unmute(this, 0);
|
|
|
|
stac9221_gpio_unmute(this, 1);
|
2007-01-06 21:35:35 +03:00
|
|
|
}
|
2007-03-11 16:31:36 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
stac9221_gpio_unmute(codec_t *this, int pin)
|
|
|
|
{
|
|
|
|
uint32_t data, mask, dir;
|
|
|
|
|
2007-05-13 07:28:19 +04:00
|
|
|
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);
|
2007-03-11 16:31:36 +03:00
|
|
|
data &= ~(1 << pin);
|
|
|
|
mask |= 1 << pin;
|
|
|
|
dir |= 1 << pin;
|
2007-05-13 07:28:19 +04:00
|
|
|
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);
|
2007-03-11 16:31:36 +03:00
|
|
|
DELAY(1000);
|
2007-05-13 07:28:19 +04:00
|
|
|
this->comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
|
2007-01-06 21:35:35 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-06-11 11:52:00 +04:00
|
|
|
/* ----------------------------------------------------------------
|
2007-05-08 09:03:50 +04:00
|
|
|
* Sigmatel STAC9200 and STAC9200D
|
2006-06-11 11:52:00 +04:00
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
2007-05-08 09:03:50 +04:00
|
|
|
static const mixer_item_t stac9200_mixer_items[] = {
|
2006-06-25 17:41:58 +04:00
|
|
|
AZ_MIXER_CLASSES,
|
2006-06-11 11:52:00 +04:00
|
|
|
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-11 11:52:00 +04:00
|
|
|
0x09, MI_TARGET_CONNLIST}, /* AudioNdac is not accurate name */
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {"selector."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0a, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-11 11:52:00 +04:00
|
|
|
0x0c, MI_TARGET_CONNLIST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNspeaker".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_PINBOOST},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmono"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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,
|
2006-06-11 11:52:00 +04:00
|
|
|
0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{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}}}},
|
2006-06-11 11:52:00 +04:00
|
|
|
0, MI_TARGET_DAC},
|
2006-09-04 02:17:35 +04:00
|
|
|
{{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD,
|
|
|
|
0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}},
|
2006-06-11 11:52:00 +04:00
|
|
|
0, MI_TARGET_ADC},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2007-05-08 09:03:50 +04:00
|
|
|
stac9200_mixer_init(codec_t *this)
|
2006-06-11 11:52:00 +04:00
|
|
|
{
|
|
|
|
mixer_ctrl_t mc;
|
2007-03-11 16:34:40 +03:00
|
|
|
uint32_t value;
|
2006-06-11 11:52:00 +04:00
|
|
|
|
2008-01-03 21:00:43 +03:00
|
|
|
this->nmixers = __arraycount(stac9200_mixer_items);
|
|
|
|
this->mixers = malloc(sizeof(stac9200_mixer_items), M_DEVBUF, M_NOWAIT);
|
2006-06-11 11:52:00 +04:00
|
|
|
if (this->mixers == NULL) {
|
2009-01-18 13:40:40 +03:00
|
|
|
aprint_error_dev(this->dev, "out of memory in %s\n", __func__);
|
2006-06-11 11:52:00 +04:00
|
|
|
return ENOMEM;
|
|
|
|
}
|
2008-01-03 21:00:43 +03:00
|
|
|
memcpy(this->mixers, stac9200_mixer_items, sizeof(stac9200_mixer_items));
|
2006-06-11 11:52:00 +04:00
|
|
|
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);
|
2007-03-11 16:34:40 +03:00
|
|
|
|
2007-05-10 11:48:16 +04:00
|
|
|
#define STAC9200_DELL_INSPIRON6400_ID 0x01bd1028
|
2007-05-08 09:03:50 +04:00
|
|
|
#define STAC9200_DELL_INSPIRON9400_ID 0x01cd1028
|
|
|
|
#define STAC9200_DELL_640M_ID 0x01d81028
|
2007-11-26 18:47:36 +03:00
|
|
|
#define STAC9200_DELL_LATITUDE_D420_ID 0x01d61028
|
2007-10-30 22:36:10 +03:00
|
|
|
#define STAC9200_DELL_LATITUDE_D430_ID 0x02011028
|
2007-11-26 18:47:36 +03:00
|
|
|
|
2007-05-08 09:03:50 +04:00
|
|
|
#define STAC9200_EVENT_HP 0
|
|
|
|
#define STAC9200_NID_HP 0x0d
|
|
|
|
#define STAC9200_NID_SPEAKER 0x0e
|
2007-11-26 18:47:36 +03:00
|
|
|
|
|
|
|
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:
|
2007-05-10 09:44:37 +04:00
|
|
|
/* Does every DELL model have the same pin configuration?
|
|
|
|
* I'm not sure. */
|
|
|
|
|
2007-03-11 16:34:40 +03:00
|
|
|
/* setup a unsolicited event for the headphones */
|
2007-05-08 09:03:50 +04:00
|
|
|
this->comresp(this, STAC9200_NID_HP, CORB_SET_UNSOLICITED_RESPONSE,
|
|
|
|
CORB_UNSOL_ENABLE | STAC9200_EVENT_HP, NULL);
|
2007-03-11 16:34:40 +03:00
|
|
|
/* If the headphone presents, mute the internal speaker */
|
2007-05-08 09:03:50 +04:00
|
|
|
this->comresp(this, STAC9200_NID_HP, CORB_GET_PIN_SENSE, 0, &value);
|
2007-05-08 09:18:20 +04:00
|
|
|
if (value & CORB_PS_PRESENCE) {
|
2007-05-08 09:03:50 +04:00
|
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
|
|
|
|
} else {
|
|
|
|
generic_mixer_pinctrl(this,
|
|
|
|
STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
|
2007-03-11 16:34:40 +03:00
|
|
|
}
|
2007-11-26 18:47:36 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2007-03-11 16:34:40 +03:00
|
|
|
}
|
2007-05-08 09:03:50 +04:00
|
|
|
return 0;
|
2007-03-11 16:34:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-05-08 09:03:50 +04:00
|
|
|
stac9200_unsol_event(codec_t *this, int tag)
|
2007-03-11 16:34:40 +03:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
uint32_t value;
|
|
|
|
|
|
|
|
switch (tag) {
|
2007-05-08 09:03:50 +04:00
|
|
|
case STAC9200_EVENT_HP:
|
|
|
|
err = this->comresp(this, STAC9200_NID_HP,
|
2007-03-11 16:34:40 +03:00
|
|
|
CORB_GET_PIN_SENSE, 0, &value);
|
|
|
|
if (err)
|
|
|
|
break;
|
2007-05-08 09:18:20 +04:00
|
|
|
if (value & CORB_PS_PRESENCE) {
|
2007-03-11 16:34:40 +03:00
|
|
|
DPRINTF(("%s: headphone has been inserted.\n", __func__));
|
2007-05-08 09:03:50 +04:00
|
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0);
|
2007-03-11 16:34:40 +03:00
|
|
|
} else {
|
|
|
|
DPRINTF(("%s: headphone has been pulled out.\n", __func__));
|
2007-05-08 09:03:50 +04:00
|
|
|
generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT);
|
2007-03-11 16:34:40 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: unknown tag: %d\n", __func__, tag);
|
|
|
|
}
|
2005-09-28 18:26:19 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2008-01-03 20:39:03 +03:00
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ATI HDMI
|
|
|
|
* ---------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int
|
|
|
|
atihdmi_init_dacgroup(codec_t *this)
|
|
|
|
{
|
|
|
|
static const convgroupset_t dacs = {
|
|
|
|
-1, 1,
|
|
|
|
{{1, {0x02}}}}; /* digital */
|
2008-06-24 14:17:00 +04:00
|
|
|
static const convgroupset_t adcs = {
|
|
|
|
-1, 0,
|
|
|
|
{{0, {0x00}}}}; /* no recording */
|
2008-01-03 20:39:03 +03:00
|
|
|
|
|
|
|
this->dacs = dacs;
|
|
|
|
this->adcs = adcs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|