xref: /illumos-gate/usr/src/cmd/bhyve/hda_codec.c (revision 32640292339b07090f10ce34d455f98711077343)
184659b24SMichael Zeller /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
384659b24SMichael Zeller  *
484659b24SMichael Zeller  * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
584659b24SMichael Zeller  * All rights reserved.
684659b24SMichael Zeller  *
784659b24SMichael Zeller  * Redistribution and use in source and binary forms, with or without
884659b24SMichael Zeller  * modification, are permitted provided that the following conditions
984659b24SMichael Zeller  * are met:
1084659b24SMichael Zeller  * 1. Redistributions of source code must retain the above copyright
1184659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer.
1284659b24SMichael Zeller  * 2. Redistributions in binary form must reproduce the above copyright
1384659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer in the
1484659b24SMichael Zeller  *    documentation and/or other materials provided with the distribution.
1584659b24SMichael Zeller  *
1684659b24SMichael Zeller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1784659b24SMichael Zeller  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1884659b24SMichael Zeller  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1984659b24SMichael Zeller  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2084659b24SMichael Zeller  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2184659b24SMichael Zeller  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2284659b24SMichael Zeller  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2384659b24SMichael Zeller  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2484659b24SMichael Zeller  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2584659b24SMichael Zeller  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2684659b24SMichael Zeller  * SUCH DAMAGE.
2784659b24SMichael Zeller  *
2884659b24SMichael Zeller  */
2984659b24SMichael Zeller 
3084659b24SMichael Zeller #include <sys/cdefs.h>
3184659b24SMichael Zeller 
3284659b24SMichael Zeller #include <pthread.h>
3384659b24SMichael Zeller #include <pthread_np.h>
3484659b24SMichael Zeller #include <unistd.h>
3584659b24SMichael Zeller 
3684659b24SMichael Zeller #include "pci_hda.h"
3784659b24SMichael Zeller #include "audio.h"
3884659b24SMichael Zeller 
3984659b24SMichael Zeller /*
4084659b24SMichael Zeller  * HDA Codec defines
4184659b24SMichael Zeller  */
4284659b24SMichael Zeller #define INTEL_VENDORID				0x8086
4384659b24SMichael Zeller 
4484659b24SMichael Zeller #define HDA_CODEC_SUBSYSTEM_ID			((INTEL_VENDORID << 16) | 0x01)
4584659b24SMichael Zeller #define HDA_CODEC_ROOT_NID			0x00
4684659b24SMichael Zeller #define HDA_CODEC_FG_NID			0x01
4784659b24SMichael Zeller #define HDA_CODEC_AUDIO_OUTPUT_NID		0x02
4884659b24SMichael Zeller #define HDA_CODEC_PIN_OUTPUT_NID		0x03
4984659b24SMichael Zeller #define HDA_CODEC_AUDIO_INPUT_NID		0x04
5084659b24SMichael Zeller #define HDA_CODEC_PIN_INPUT_NID			0x05
5184659b24SMichael Zeller 
5284659b24SMichael Zeller #define HDA_CODEC_STREAMS_COUNT			0x02
5384659b24SMichael Zeller #define HDA_CODEC_STREAM_OUTPUT			0x00
5484659b24SMichael Zeller #define HDA_CODEC_STREAM_INPUT			0x01
5584659b24SMichael Zeller 
5684659b24SMichael Zeller #define HDA_CODEC_PARAMS_COUNT			0x14
5784659b24SMichael Zeller #define HDA_CODEC_CONN_LIST_COUNT		0x01
5884659b24SMichael Zeller #define HDA_CODEC_RESPONSE_EX_UNSOL		0x10
5984659b24SMichael Zeller #define HDA_CODEC_RESPONSE_EX_SOL		0x00
6084659b24SMichael Zeller #define HDA_CODEC_AMP_NUMSTEPS			0x4a
6184659b24SMichael Zeller 
6284659b24SMichael Zeller #define HDA_CODEC_SUPP_STREAM_FORMATS_PCM				\
6384659b24SMichael Zeller 	(1 << HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT)
6484659b24SMichael Zeller 
6584659b24SMichael Zeller #define HDA_CODEC_FMT_BASE_MASK			(0x01 << 14)
6684659b24SMichael Zeller 
6784659b24SMichael Zeller #define HDA_CODEC_FMT_MULT_MASK			(0x07 << 11)
6884659b24SMichael Zeller #define HDA_CODEC_FMT_MULT_2			(0x01 << 11)
6984659b24SMichael Zeller #define HDA_CODEC_FMT_MULT_3			(0x02 << 11)
7084659b24SMichael Zeller #define HDA_CODEC_FMT_MULT_4			(0x03 << 11)
7184659b24SMichael Zeller 
7284659b24SMichael Zeller #define HDA_CODEC_FMT_DIV_MASK			0x07
7384659b24SMichael Zeller #define HDA_CODEC_FMT_DIV_SHIFT			8
7484659b24SMichael Zeller 
7584659b24SMichael Zeller #define HDA_CODEC_FMT_BITS_MASK			(0x07 << 4)
7684659b24SMichael Zeller #define HDA_CODEC_FMT_BITS_8			(0x00 << 4)
7784659b24SMichael Zeller #define HDA_CODEC_FMT_BITS_16			(0x01 << 4)
7884659b24SMichael Zeller #define HDA_CODEC_FMT_BITS_24			(0x03 << 4)
7984659b24SMichael Zeller #define HDA_CODEC_FMT_BITS_32			(0x04 << 4)
8084659b24SMichael Zeller 
8184659b24SMichael Zeller #define HDA_CODEC_FMT_CHAN_MASK			(0x0f << 0)
8284659b24SMichael Zeller 
8384659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_OUTPUT					\
8484659b24SMichael Zeller 	(0x00 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
8584659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_INPUT					\
8684659b24SMichael Zeller 	(0x01 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
8784659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_PIN					\
8884659b24SMichael Zeller 	(0x04 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
8984659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_CONN_LIST					\
9084659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT)
9184659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_FORMAT_OVR					\
9284659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT)
9384659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_AMP_OVR					\
9484659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT)
9584659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_OUT_AMP					\
9684659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT)
9784659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_IN_AMP					\
9884659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT)
9984659b24SMichael Zeller #define HDA_CODEC_AUDIO_WCAP_STEREO					\
10084659b24SMichael Zeller 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT)
10184659b24SMichael Zeller 
10284659b24SMichael Zeller #define HDA_CODEC_PIN_CAP_OUTPUT					\
10384659b24SMichael Zeller 	(1 << HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT)
10484659b24SMichael Zeller #define HDA_CODEC_PIN_CAP_INPUT						\
10584659b24SMichael Zeller 	(1 << HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT)
10684659b24SMichael Zeller #define HDA_CODEC_PIN_CAP_PRESENCE_DETECT				\
10784659b24SMichael Zeller 	(1 << HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT)
10884659b24SMichael Zeller 
10984659b24SMichael Zeller #define HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP				\
11084659b24SMichael Zeller 	(1 << HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT)
11184659b24SMichael Zeller #define HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE				\
11284659b24SMichael Zeller 	(0x03 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT)
11384659b24SMichael Zeller #define HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS				\
11484659b24SMichael Zeller 	(HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT)
11584659b24SMichael Zeller #define HDA_CODEC_OUTPUT_AMP_CAP_OFFSET					\
11684659b24SMichael Zeller 	(HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT)
11784659b24SMichael Zeller 
11884659b24SMichael Zeller #define HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE	0x80
11984659b24SMichael Zeller #define HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK	0x7f
12084659b24SMichael Zeller 
12184659b24SMichael Zeller #define HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED	(1 << 31)
12284659b24SMichael Zeller #define HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE				\
12384659b24SMichael Zeller 	(1 << HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT)
12484659b24SMichael Zeller #define HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE				\
12584659b24SMichael Zeller 	(1 << HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT)
12684659b24SMichael Zeller 
12784659b24SMichael Zeller #define HDA_CONFIG_DEFAULTCONF_COLOR_BLACK				\
12884659b24SMichael Zeller 	(0x01 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
12984659b24SMichael Zeller #define HDA_CONFIG_DEFAULTCONF_COLOR_RED				\
13084659b24SMichael Zeller 	(0x05 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
13184659b24SMichael Zeller 
13284659b24SMichael Zeller #define HDA_CODEC_BUF_SIZE			HDA_FIFO_SIZE
13384659b24SMichael Zeller 
13484659b24SMichael Zeller #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
13584659b24SMichael Zeller 
13684659b24SMichael Zeller 
13784659b24SMichael Zeller /*
13884659b24SMichael Zeller  * HDA Audio Context data structures
13984659b24SMichael Zeller  */
14084659b24SMichael Zeller 
14184659b24SMichael Zeller typedef void (*transfer_func_t)(void *arg);
14284659b24SMichael Zeller typedef int (*setup_func_t)(void *arg);
14384659b24SMichael Zeller 
14484659b24SMichael Zeller struct hda_audio_ctxt {
14584659b24SMichael Zeller 	char name[64];
14684659b24SMichael Zeller 	uint8_t run;
14784659b24SMichael Zeller 	uint8_t started;
14884659b24SMichael Zeller 	void *priv;
14984659b24SMichael Zeller 	pthread_t tid;
15084659b24SMichael Zeller 	pthread_mutex_t mtx;
15184659b24SMichael Zeller 	pthread_cond_t cond;
15284659b24SMichael Zeller 	setup_func_t do_setup;
15384659b24SMichael Zeller 	transfer_func_t do_transfer;
15484659b24SMichael Zeller };
15584659b24SMichael Zeller 
15684659b24SMichael Zeller /*
15784659b24SMichael Zeller  * HDA Audio Context module function declarations
15884659b24SMichael Zeller  */
15984659b24SMichael Zeller 
16084659b24SMichael Zeller static void *hda_audio_ctxt_thr(void *arg);
16184659b24SMichael Zeller static int hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
16284659b24SMichael Zeller     transfer_func_t do_transfer, setup_func_t do_setup, void *priv);
16384659b24SMichael Zeller static int hda_audio_ctxt_start(struct hda_audio_ctxt *actx);
16484659b24SMichael Zeller static int hda_audio_ctxt_stop(struct hda_audio_ctxt *actx);
16584659b24SMichael Zeller 
16684659b24SMichael Zeller /*
16784659b24SMichael Zeller  * HDA Codec data structures
16884659b24SMichael Zeller  */
16984659b24SMichael Zeller 
17084659b24SMichael Zeller struct hda_codec_softc;
17184659b24SMichael Zeller 
17284659b24SMichael Zeller typedef uint32_t (*verb_func_t)(struct hda_codec_softc *sc, uint16_t verb,
17384659b24SMichael Zeller 				    uint16_t payload);
17484659b24SMichael Zeller 
17584659b24SMichael Zeller struct hda_codec_stream {
17684659b24SMichael Zeller 	uint8_t buf[HDA_CODEC_BUF_SIZE];
17784659b24SMichael Zeller 	uint8_t channel;
17884659b24SMichael Zeller 	uint16_t fmt;
17984659b24SMichael Zeller 	uint8_t stream;
18084659b24SMichael Zeller 
18184659b24SMichael Zeller 	uint8_t left_gain;
18284659b24SMichael Zeller 	uint8_t right_gain;
18384659b24SMichael Zeller 	uint8_t left_mute;
18484659b24SMichael Zeller 	uint8_t right_mute;
18584659b24SMichael Zeller 
18684659b24SMichael Zeller 	struct audio *aud;
18784659b24SMichael Zeller 	struct hda_audio_ctxt actx;
18884659b24SMichael Zeller };
18984659b24SMichael Zeller 
19084659b24SMichael Zeller struct hda_codec_softc {
19184659b24SMichael Zeller 	uint32_t no_nodes;
19284659b24SMichael Zeller 	uint32_t subsystem_id;
19384659b24SMichael Zeller 	const uint32_t (*get_parameters)[HDA_CODEC_PARAMS_COUNT];
19484659b24SMichael Zeller 	const uint8_t (*conn_list)[HDA_CODEC_CONN_LIST_COUNT];
19584659b24SMichael Zeller 	const uint32_t *conf_default;
19684659b24SMichael Zeller 	const uint8_t *pin_ctrl_default;
19784659b24SMichael Zeller 	const verb_func_t *verb_handlers;
19884659b24SMichael Zeller 
19984659b24SMichael Zeller 	struct hda_codec_inst *hci;
20084659b24SMichael Zeller 	struct hda_codec_stream streams[HDA_CODEC_STREAMS_COUNT];
20184659b24SMichael Zeller };
20284659b24SMichael Zeller 
20384659b24SMichael Zeller /*
20484659b24SMichael Zeller  * HDA Codec module function declarations
20584659b24SMichael Zeller  */
20684659b24SMichael Zeller static int hda_codec_init(struct hda_codec_inst *hci, const char *play,
2072b948146SAndy Fiddaman     const char *rec);
20884659b24SMichael Zeller static int hda_codec_reset(struct hda_codec_inst *hci);
20984659b24SMichael Zeller static int hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data);
21084659b24SMichael Zeller static int hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
21184659b24SMichael Zeller     uint8_t stream, uint8_t dir);
21284659b24SMichael Zeller 
21384659b24SMichael Zeller static int hda_codec_parse_format(uint16_t fmt, struct audio_params *params);
21484659b24SMichael Zeller 
21584659b24SMichael Zeller static uint32_t hda_codec_audio_output_nid(struct hda_codec_softc *sc,
21684659b24SMichael Zeller     uint16_t verb, uint16_t payload);
21784659b24SMichael Zeller static void hda_codec_audio_output_do_transfer(void *arg);
21884659b24SMichael Zeller static int hda_codec_audio_output_do_setup(void *arg);
21984659b24SMichael Zeller static uint32_t hda_codec_audio_input_nid(struct hda_codec_softc *sc,
22084659b24SMichael Zeller     uint16_t verb, uint16_t payload);
22184659b24SMichael Zeller static void hda_codec_audio_input_do_transfer(void *arg);
22284659b24SMichael Zeller static int hda_codec_audio_input_do_setup(void *arg);
22384659b24SMichael Zeller 
22484659b24SMichael Zeller static uint32_t hda_codec_audio_inout_nid(struct hda_codec_stream *st,
22584659b24SMichael Zeller     uint16_t verb, uint16_t payload);
22684659b24SMichael Zeller 
22784659b24SMichael Zeller /*
22884659b24SMichael Zeller  * HDA Codec global data
22984659b24SMichael Zeller  */
23084659b24SMichael Zeller 
23184659b24SMichael Zeller #define HDA_CODEC_ROOT_DESC						\
23284659b24SMichael Zeller 	[HDA_CODEC_ROOT_NID] = {					\
23384659b24SMichael Zeller 		[HDA_PARAM_VENDOR_ID] = INTEL_VENDORID,			\
23484659b24SMichael Zeller 		[HDA_PARAM_REVISION_ID] = 0xffff,			\
23584659b24SMichael Zeller 		/* 1 Subnode, StartNid = 1 */				\
23684659b24SMichael Zeller 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00010001,		\
23784659b24SMichael Zeller 	},								\
23884659b24SMichael Zeller 
23984659b24SMichael Zeller #define HDA_CODEC_FG_COMMON_DESC					\
24084659b24SMichael Zeller 	[HDA_PARAM_FCT_GRP_TYPE] = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO,\
24184659b24SMichael Zeller 	/* B8 - B32, 8.0 - 192.0kHz */					\
24284659b24SMichael Zeller 	[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x1f << 16) | 0x7ff,		\
24384659b24SMichael Zeller 	[HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM,\
24484659b24SMichael Zeller 	[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */		\
24584659b24SMichael Zeller 	[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */		\
24684659b24SMichael Zeller 	[HDA_PARAM_GPIO_COUNT] = 0x00,					\
24784659b24SMichael Zeller 
24884659b24SMichael Zeller #define HDA_CODEC_FG_OUTPUT_DESC					\
24984659b24SMichael Zeller 	[HDA_CODEC_FG_NID] = {						\
25084659b24SMichael Zeller 		/* 2 Subnodes, StartNid = 2 */				\
25184659b24SMichael Zeller 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00020002,		\
25284659b24SMichael Zeller 		HDA_CODEC_FG_COMMON_DESC				\
25384659b24SMichael Zeller 	},								\
25484659b24SMichael Zeller 
25584659b24SMichael Zeller #define HDA_CODEC_FG_INPUT_DESC						\
25684659b24SMichael Zeller 	[HDA_CODEC_FG_NID] = {						\
25784659b24SMichael Zeller 		/* 2 Subnodes, StartNid = 4 */				\
25884659b24SMichael Zeller 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00040002,		\
25984659b24SMichael Zeller 		HDA_CODEC_FG_COMMON_DESC				\
26084659b24SMichael Zeller 	},								\
26184659b24SMichael Zeller 
26284659b24SMichael Zeller #define HDA_CODEC_FG_DUPLEX_DESC					\
26384659b24SMichael Zeller 	[HDA_CODEC_FG_NID] = {						\
26484659b24SMichael Zeller 		/* 4 Subnodes, StartNid = 2 */				\
26584659b24SMichael Zeller 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00020004,		\
26684659b24SMichael Zeller 		HDA_CODEC_FG_COMMON_DESC				\
26784659b24SMichael Zeller 	},								\
26884659b24SMichael Zeller 
26984659b24SMichael Zeller #define HDA_CODEC_OUTPUT_DESC						\
27084659b24SMichael Zeller 	[HDA_CODEC_AUDIO_OUTPUT_NID] = {				\
27184659b24SMichael Zeller 		[HDA_PARAM_AUDIO_WIDGET_CAP] = 				\
27284659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_OUTPUT |		\
27384659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_FORMAT_OVR |	\
27484659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_AMP_OVR |		\
27584659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_OUT_AMP |		\
27684659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
27784659b24SMichael Zeller 		/* B16, 16.0 - 192.0kHz */				\
27884659b24SMichael Zeller 		[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc,	\
27984659b24SMichael Zeller 		[HDA_PARAM_SUPP_STREAM_FORMATS] =			\
28084659b24SMichael Zeller 				HDA_CODEC_SUPP_STREAM_FORMATS_PCM,	\
28184659b24SMichael Zeller 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
28284659b24SMichael Zeller 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x00,			\
28384659b24SMichael Zeller 		[HDA_PARAM_OUTPUT_AMP_CAP] =				\
28484659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP |	\
28584659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE |	\
28684659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS |	\
28784659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_OFFSET,	\
28884659b24SMichael Zeller 	},								\
28984659b24SMichael Zeller 	[HDA_CODEC_PIN_OUTPUT_NID] = {					\
29084659b24SMichael Zeller 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
29184659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_PIN |		\
29284659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_CONN_LIST |	\
29384659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
29484659b24SMichael Zeller 		[HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_OUTPUT |	\
29584659b24SMichael Zeller 				      HDA_CODEC_PIN_CAP_PRESENCE_DETECT,\
29684659b24SMichael Zeller 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
29784659b24SMichael Zeller 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x01,			\
29884659b24SMichael Zeller 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
29984659b24SMichael Zeller 	},								\
30084659b24SMichael Zeller 
30184659b24SMichael Zeller #define HDA_CODEC_INPUT_DESC						\
30284659b24SMichael Zeller 	[HDA_CODEC_AUDIO_INPUT_NID] = {					\
30384659b24SMichael Zeller 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
30484659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_INPUT |		\
30584659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_CONN_LIST |	\
30684659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_FORMAT_OVR |	\
30784659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_AMP_OVR |		\
30884659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_IN_AMP |		\
30984659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
31084659b24SMichael Zeller 		/* B16, 16.0 - 192.0kHz */				\
31184659b24SMichael Zeller 		[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc,	\
31284659b24SMichael Zeller 		[HDA_PARAM_SUPP_STREAM_FORMATS] =			\
31384659b24SMichael Zeller 				HDA_CODEC_SUPP_STREAM_FORMATS_PCM,	\
31484659b24SMichael Zeller 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
31584659b24SMichael Zeller 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x01,			\
31684659b24SMichael Zeller 		[HDA_PARAM_INPUT_AMP_CAP] =				\
31784659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP |	\
31884659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE |	\
31984659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS |	\
32084659b24SMichael Zeller 				HDA_CODEC_OUTPUT_AMP_CAP_OFFSET,	\
32184659b24SMichael Zeller 	},								\
32284659b24SMichael Zeller 	[HDA_CODEC_PIN_INPUT_NID] = {					\
32384659b24SMichael Zeller 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
32484659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_PIN |		\
32584659b24SMichael Zeller 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
32684659b24SMichael Zeller 		[HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_INPUT |		\
32784659b24SMichael Zeller 				HDA_CODEC_PIN_CAP_PRESENCE_DETECT,	\
32884659b24SMichael Zeller 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
32984659b24SMichael Zeller 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
33084659b24SMichael Zeller 	},								\
33184659b24SMichael Zeller 
33284659b24SMichael Zeller static const uint32_t
33384659b24SMichael Zeller hda_codec_output_parameters[][HDA_CODEC_PARAMS_COUNT] = {
33484659b24SMichael Zeller 	HDA_CODEC_ROOT_DESC
33584659b24SMichael Zeller 	HDA_CODEC_FG_OUTPUT_DESC
33684659b24SMichael Zeller 	HDA_CODEC_OUTPUT_DESC
33784659b24SMichael Zeller };
33884659b24SMichael Zeller 
33984659b24SMichael Zeller static const uint32_t
34084659b24SMichael Zeller hda_codec_input_parameters[][HDA_CODEC_PARAMS_COUNT] = {
34184659b24SMichael Zeller 	HDA_CODEC_ROOT_DESC
34284659b24SMichael Zeller 	HDA_CODEC_FG_INPUT_DESC
34384659b24SMichael Zeller 	HDA_CODEC_INPUT_DESC
34484659b24SMichael Zeller };
34584659b24SMichael Zeller 
34684659b24SMichael Zeller static const uint32_t
34784659b24SMichael Zeller hda_codec_duplex_parameters[][HDA_CODEC_PARAMS_COUNT] = {
34884659b24SMichael Zeller 	HDA_CODEC_ROOT_DESC
34984659b24SMichael Zeller 	HDA_CODEC_FG_DUPLEX_DESC
35084659b24SMichael Zeller 	HDA_CODEC_OUTPUT_DESC
35184659b24SMichael Zeller 	HDA_CODEC_INPUT_DESC
35284659b24SMichael Zeller };
35384659b24SMichael Zeller 
35484659b24SMichael Zeller #define HDA_CODEC_NODES_COUNT	(ARRAY_SIZE(hda_codec_duplex_parameters))
35584659b24SMichael Zeller 
35684659b24SMichael Zeller static const uint8_t
35784659b24SMichael Zeller hda_codec_conn_list[HDA_CODEC_NODES_COUNT][HDA_CODEC_CONN_LIST_COUNT] = {
35884659b24SMichael Zeller 	[HDA_CODEC_PIN_OUTPUT_NID] = {HDA_CODEC_AUDIO_OUTPUT_NID},
35984659b24SMichael Zeller 	[HDA_CODEC_AUDIO_INPUT_NID] = {HDA_CODEC_PIN_INPUT_NID},
36084659b24SMichael Zeller };
36184659b24SMichael Zeller 
36284659b24SMichael Zeller static const uint32_t
36384659b24SMichael Zeller hda_codec_conf_default[HDA_CODEC_NODES_COUNT] = {
36484659b24SMichael Zeller 	[HDA_CODEC_PIN_OUTPUT_NID] =					\
36584659b24SMichael Zeller 		HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
36684659b24SMichael Zeller 		HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT |
36784659b24SMichael Zeller 		HDA_CONFIG_DEFAULTCONF_COLOR_BLACK |
36884659b24SMichael Zeller 		(0x01 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
36984659b24SMichael Zeller 	[HDA_CODEC_PIN_INPUT_NID] = HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
37084659b24SMichael Zeller 				    HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN |
37184659b24SMichael Zeller 				    HDA_CONFIG_DEFAULTCONF_COLOR_RED |
37284659b24SMichael Zeller 			(0x02 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
37384659b24SMichael Zeller };
37484659b24SMichael Zeller 
37584659b24SMichael Zeller static const uint8_t
37684659b24SMichael Zeller hda_codec_pin_ctrl_default[HDA_CODEC_NODES_COUNT] = {
37784659b24SMichael Zeller 	[HDA_CODEC_PIN_OUTPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE,
37884659b24SMichael Zeller 	[HDA_CODEC_PIN_INPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE,
37984659b24SMichael Zeller };
38084659b24SMichael Zeller 
38184659b24SMichael Zeller static const
38284659b24SMichael Zeller verb_func_t hda_codec_verb_handlers[HDA_CODEC_NODES_COUNT] = {
38384659b24SMichael Zeller 	[HDA_CODEC_AUDIO_OUTPUT_NID] = hda_codec_audio_output_nid,
38484659b24SMichael Zeller 	[HDA_CODEC_AUDIO_INPUT_NID] = hda_codec_audio_input_nid,
38584659b24SMichael Zeller };
38684659b24SMichael Zeller 
38784659b24SMichael Zeller /*
38884659b24SMichael Zeller  * HDA Codec module function definitions
38984659b24SMichael Zeller  */
39084659b24SMichael Zeller 
39184659b24SMichael Zeller static int
hda_codec_init(struct hda_codec_inst * hci,const char * play,const char * rec)39284659b24SMichael Zeller hda_codec_init(struct hda_codec_inst *hci, const char *play,
3932b948146SAndy Fiddaman     const char *rec)
39484659b24SMichael Zeller {
39584659b24SMichael Zeller 	struct hda_codec_softc *sc = NULL;
39684659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
39784659b24SMichael Zeller 	int err;
39884659b24SMichael Zeller 
39984659b24SMichael Zeller 	if (!(play || rec))
40084659b24SMichael Zeller 		return (-1);
40184659b24SMichael Zeller 
40284659b24SMichael Zeller 	sc = calloc(1, sizeof(*sc));
40384659b24SMichael Zeller 	if (!sc)
40484659b24SMichael Zeller 		return (-1);
40584659b24SMichael Zeller 
40684659b24SMichael Zeller 	if (play && rec)
40784659b24SMichael Zeller 		sc->get_parameters = hda_codec_duplex_parameters;
40884659b24SMichael Zeller 	else {
40984659b24SMichael Zeller 		if (play)
41084659b24SMichael Zeller 			sc->get_parameters = hda_codec_output_parameters;
41184659b24SMichael Zeller 		else
41284659b24SMichael Zeller 			sc->get_parameters = hda_codec_input_parameters;
41384659b24SMichael Zeller 	}
41484659b24SMichael Zeller 	sc->subsystem_id = HDA_CODEC_SUBSYSTEM_ID;
41584659b24SMichael Zeller 	sc->no_nodes = HDA_CODEC_NODES_COUNT;
41684659b24SMichael Zeller 	sc->conn_list = hda_codec_conn_list;
41784659b24SMichael Zeller 	sc->conf_default = hda_codec_conf_default;
41884659b24SMichael Zeller 	sc->pin_ctrl_default = hda_codec_pin_ctrl_default;
41984659b24SMichael Zeller 	sc->verb_handlers = hda_codec_verb_handlers;
420154972afSPatrick Mooney 	DPRINTF("HDA Codec nodes: %d", sc->no_nodes);
42184659b24SMichael Zeller 
42284659b24SMichael Zeller 	/*
42384659b24SMichael Zeller 	 * Initialize the Audio Output stream
42484659b24SMichael Zeller 	 */
42584659b24SMichael Zeller 	if (play) {
42684659b24SMichael Zeller 		st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
42784659b24SMichael Zeller 
42884659b24SMichael Zeller 		err = hda_audio_ctxt_init(&st->actx, "hda-audio-output",
42984659b24SMichael Zeller 			hda_codec_audio_output_do_transfer,
43084659b24SMichael Zeller 			hda_codec_audio_output_do_setup, sc);
43184659b24SMichael Zeller 		assert(!err);
43284659b24SMichael Zeller 
43384659b24SMichael Zeller 		st->aud = audio_init(play, 1);
43484659b24SMichael Zeller 		if (!st->aud) {
435154972afSPatrick Mooney 			DPRINTF("Fail to init the output audio player");
43684659b24SMichael Zeller 			return (-1);
43784659b24SMichael Zeller 		}
43884659b24SMichael Zeller 	}
43984659b24SMichael Zeller 
44084659b24SMichael Zeller 	/*
44184659b24SMichael Zeller 	 * Initialize the Audio Input stream
44284659b24SMichael Zeller 	 */
44384659b24SMichael Zeller 	if (rec) {
44484659b24SMichael Zeller 		st = &sc->streams[HDA_CODEC_STREAM_INPUT];
44584659b24SMichael Zeller 
44684659b24SMichael Zeller 		err = hda_audio_ctxt_init(&st->actx, "hda-audio-input",
44784659b24SMichael Zeller 			hda_codec_audio_input_do_transfer,
44884659b24SMichael Zeller 			hda_codec_audio_input_do_setup, sc);
44984659b24SMichael Zeller 		assert(!err);
45084659b24SMichael Zeller 
45184659b24SMichael Zeller 		st->aud = audio_init(rec, 0);
45284659b24SMichael Zeller 		if (!st->aud) {
453154972afSPatrick Mooney 			DPRINTF("Fail to init the input audio player");
45484659b24SMichael Zeller 			return (-1);
45584659b24SMichael Zeller 		}
45684659b24SMichael Zeller 	}
45784659b24SMichael Zeller 
45884659b24SMichael Zeller 	sc->hci = hci;
45984659b24SMichael Zeller 	hci->priv = sc;
46084659b24SMichael Zeller 
46184659b24SMichael Zeller 	return (0);
46284659b24SMichael Zeller }
46384659b24SMichael Zeller 
46484659b24SMichael Zeller static int
hda_codec_reset(struct hda_codec_inst * hci)46584659b24SMichael Zeller hda_codec_reset(struct hda_codec_inst *hci)
46684659b24SMichael Zeller {
46759d65d31SAndy Fiddaman 	const struct hda_ops *hops = NULL;
46884659b24SMichael Zeller 	struct hda_codec_softc *sc = NULL;
46984659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
47084659b24SMichael Zeller 	int i;
47184659b24SMichael Zeller 
47284659b24SMichael Zeller 	assert(hci);
47384659b24SMichael Zeller 
47484659b24SMichael Zeller 	hops = hci->hops;
47584659b24SMichael Zeller 	assert(hops);
47684659b24SMichael Zeller 
47784659b24SMichael Zeller 	sc = (struct hda_codec_softc *)hci->priv;
47884659b24SMichael Zeller 	assert(sc);
47984659b24SMichael Zeller 
48084659b24SMichael Zeller 	for (i = 0; i < HDA_CODEC_STREAMS_COUNT; i++) {
48184659b24SMichael Zeller 		st = &sc->streams[i];
48284659b24SMichael Zeller 		st->left_gain = HDA_CODEC_AMP_NUMSTEPS;
48384659b24SMichael Zeller 		st->right_gain = HDA_CODEC_AMP_NUMSTEPS;
48484659b24SMichael Zeller 		st->left_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
48584659b24SMichael Zeller 		st->right_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
48684659b24SMichael Zeller 	}
48784659b24SMichael Zeller 
488154972afSPatrick Mooney 	DPRINTF("cad: 0x%x", hci->cad);
48984659b24SMichael Zeller 
49084659b24SMichael Zeller 	if (!hops->signal) {
49184659b24SMichael Zeller 		DPRINTF("The controller ops does not implement \
492154972afSPatrick Mooney 			 the signal function");
49384659b24SMichael Zeller 		return (-1);
49484659b24SMichael Zeller 	}
49584659b24SMichael Zeller 
49684659b24SMichael Zeller 	return (hops->signal(hci));
49784659b24SMichael Zeller }
49884659b24SMichael Zeller 
49984659b24SMichael Zeller static int
hda_codec_command(struct hda_codec_inst * hci,uint32_t cmd_data)50084659b24SMichael Zeller hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data)
50184659b24SMichael Zeller {
50259d65d31SAndy Fiddaman 	const struct hda_ops *hops = NULL;
50384659b24SMichael Zeller 	struct hda_codec_softc *sc = NULL;
50484659b24SMichael Zeller 	uint8_t cad = 0, nid = 0;
50584659b24SMichael Zeller 	uint16_t verb = 0, payload = 0;
50684659b24SMichael Zeller 	uint32_t res = 0;
50784659b24SMichael Zeller 
50884659b24SMichael Zeller 	/* 4 bits */
50984659b24SMichael Zeller 	cad = (cmd_data >> HDA_CMD_CAD_SHIFT) & 0x0f;
51084659b24SMichael Zeller 	/* 8 bits */
51184659b24SMichael Zeller 	nid = (cmd_data >> HDA_CMD_NID_SHIFT) & 0xff;
51284659b24SMichael Zeller 
51384659b24SMichael Zeller 	if ((cmd_data & 0x70000) == 0x70000) {
51484659b24SMichael Zeller 		/* 12 bits */
51584659b24SMichael Zeller 		verb = (cmd_data >> HDA_CMD_VERB_12BIT_SHIFT) & 0x0fff;
51684659b24SMichael Zeller 		/* 8 bits */
51784659b24SMichael Zeller 		payload = cmd_data & 0xff;
51884659b24SMichael Zeller 	} else {
51984659b24SMichael Zeller 		/* 4 bits */
52084659b24SMichael Zeller 		verb = (cmd_data >> HDA_CMD_VERB_4BIT_SHIFT) & 0x0f;
52184659b24SMichael Zeller 		/* 16 bits */
52284659b24SMichael Zeller 		payload = cmd_data & 0xffff;
52384659b24SMichael Zeller 	}
52484659b24SMichael Zeller 
52584659b24SMichael Zeller 	assert(cad == hci->cad);
52684659b24SMichael Zeller 	assert(hci);
52784659b24SMichael Zeller 
52884659b24SMichael Zeller 	hops = hci->hops;
52984659b24SMichael Zeller 	assert(hops);
53084659b24SMichael Zeller 
53184659b24SMichael Zeller 	sc = (struct hda_codec_softc *)hci->priv;
53284659b24SMichael Zeller 	assert(sc);
53384659b24SMichael Zeller 
53484659b24SMichael Zeller 	assert(nid < sc->no_nodes);
53584659b24SMichael Zeller 
53684659b24SMichael Zeller 	if (!hops->response) {
53784659b24SMichael Zeller 		DPRINTF("The controller ops does not implement \
538154972afSPatrick Mooney 			 the response function");
53984659b24SMichael Zeller 		return (-1);
54084659b24SMichael Zeller 	}
54184659b24SMichael Zeller 
54284659b24SMichael Zeller 	switch (verb) {
54384659b24SMichael Zeller 	case HDA_CMD_VERB_GET_PARAMETER:
54484659b24SMichael Zeller 		res = sc->get_parameters[nid][payload];
54584659b24SMichael Zeller 		break;
54684659b24SMichael Zeller 	case HDA_CMD_VERB_GET_CONN_LIST_ENTRY:
54784659b24SMichael Zeller 		res = sc->conn_list[nid][0];
54884659b24SMichael Zeller 		break;
54984659b24SMichael Zeller 	case HDA_CMD_VERB_GET_PIN_WIDGET_CTRL:
55084659b24SMichael Zeller 		res = sc->pin_ctrl_default[nid];
55184659b24SMichael Zeller 		break;
55284659b24SMichael Zeller 	case HDA_CMD_VERB_GET_PIN_SENSE:
55384659b24SMichael Zeller 		res = HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED;
55484659b24SMichael Zeller 		break;
55584659b24SMichael Zeller 	case HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT:
55684659b24SMichael Zeller 		res = sc->conf_default[nid];
55784659b24SMichael Zeller 		break;
55884659b24SMichael Zeller 	case HDA_CMD_VERB_GET_SUBSYSTEM_ID:
55984659b24SMichael Zeller 		res = sc->subsystem_id;
56084659b24SMichael Zeller 		break;
56184659b24SMichael Zeller 	default:
56284659b24SMichael Zeller 		assert(sc->verb_handlers);
56384659b24SMichael Zeller 		if (sc->verb_handlers[nid])
56484659b24SMichael Zeller 			res = sc->verb_handlers[nid](sc, verb, payload);
56584659b24SMichael Zeller 		else
566154972afSPatrick Mooney 			DPRINTF("Unknown VERB: 0x%x", verb);
56784659b24SMichael Zeller 		break;
56884659b24SMichael Zeller 	}
56984659b24SMichael Zeller 
570154972afSPatrick Mooney 	DPRINTF("cad: 0x%x nid: 0x%x verb: 0x%x payload: 0x%x response: 0x%x",
57184659b24SMichael Zeller 	    cad, nid, verb, payload, res);
57284659b24SMichael Zeller 
57384659b24SMichael Zeller 	return (hops->response(hci, res, HDA_CODEC_RESPONSE_EX_SOL));
57484659b24SMichael Zeller }
57584659b24SMichael Zeller 
57684659b24SMichael Zeller static int
hda_codec_notify(struct hda_codec_inst * hci,uint8_t run,uint8_t stream,uint8_t dir)57784659b24SMichael Zeller hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
57884659b24SMichael Zeller     uint8_t stream, uint8_t dir)
57984659b24SMichael Zeller {
58084659b24SMichael Zeller 	struct hda_codec_softc *sc = NULL;
58184659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
58284659b24SMichael Zeller 	struct hda_audio_ctxt *actx = NULL;
58384659b24SMichael Zeller 	int i;
58484659b24SMichael Zeller 	int err;
58584659b24SMichael Zeller 
58684659b24SMichael Zeller 	assert(hci);
58784659b24SMichael Zeller 	assert(stream);
58884659b24SMichael Zeller 
58984659b24SMichael Zeller 	sc = (struct hda_codec_softc *)hci->priv;
59084659b24SMichael Zeller 	assert(sc);
59184659b24SMichael Zeller 
59284659b24SMichael Zeller 	i = dir ? HDA_CODEC_STREAM_OUTPUT : HDA_CODEC_STREAM_INPUT;
59384659b24SMichael Zeller 	st = &sc->streams[i];
59484659b24SMichael Zeller 
595154972afSPatrick Mooney 	DPRINTF("run: %d, stream: 0x%x, st->stream: 0x%x dir: %d",
59684659b24SMichael Zeller 	    run, stream, st->stream, dir);
59784659b24SMichael Zeller 
59884659b24SMichael Zeller 	if (stream != st->stream) {
599154972afSPatrick Mooney 		DPRINTF("Stream not found");
60084659b24SMichael Zeller 		return (0);
60184659b24SMichael Zeller 	}
60284659b24SMichael Zeller 
60384659b24SMichael Zeller 	actx = &st->actx;
60484659b24SMichael Zeller 
60584659b24SMichael Zeller 	if (run)
60684659b24SMichael Zeller 		err = hda_audio_ctxt_start(actx);
60784659b24SMichael Zeller 	else
60884659b24SMichael Zeller 		err = hda_audio_ctxt_stop(actx);
60984659b24SMichael Zeller 
61084659b24SMichael Zeller 	return (err);
61184659b24SMichael Zeller }
61284659b24SMichael Zeller 
61384659b24SMichael Zeller static int
hda_codec_parse_format(uint16_t fmt,struct audio_params * params)61484659b24SMichael Zeller hda_codec_parse_format(uint16_t fmt, struct audio_params *params)
61584659b24SMichael Zeller {
61684659b24SMichael Zeller 	uint8_t div = 0;
61784659b24SMichael Zeller 
61884659b24SMichael Zeller 	assert(params);
61984659b24SMichael Zeller 
62084659b24SMichael Zeller 	/* Compute the Sample Rate */
62184659b24SMichael Zeller 	params->rate = (fmt & HDA_CODEC_FMT_BASE_MASK) ? 44100 : 48000;
62284659b24SMichael Zeller 
62384659b24SMichael Zeller 	switch (fmt & HDA_CODEC_FMT_MULT_MASK) {
62484659b24SMichael Zeller 	case HDA_CODEC_FMT_MULT_2:
62584659b24SMichael Zeller 		params->rate *= 2;
62684659b24SMichael Zeller 		break;
62784659b24SMichael Zeller 	case HDA_CODEC_FMT_MULT_3:
62884659b24SMichael Zeller 		params->rate *= 3;
62984659b24SMichael Zeller 		break;
63084659b24SMichael Zeller 	case HDA_CODEC_FMT_MULT_4:
63184659b24SMichael Zeller 		params->rate *= 4;
63284659b24SMichael Zeller 		break;
63384659b24SMichael Zeller 	}
63484659b24SMichael Zeller 
63584659b24SMichael Zeller 	div = (fmt >> HDA_CODEC_FMT_DIV_SHIFT) & HDA_CODEC_FMT_DIV_MASK;
63684659b24SMichael Zeller 	params->rate /= (div + 1);
63784659b24SMichael Zeller 
63884659b24SMichael Zeller 	/* Compute the Bits per Sample */
63984659b24SMichael Zeller 	switch (fmt & HDA_CODEC_FMT_BITS_MASK) {
64084659b24SMichael Zeller 	case HDA_CODEC_FMT_BITS_8:
64184659b24SMichael Zeller 		params->format = AFMT_U8;
64284659b24SMichael Zeller 		break;
64384659b24SMichael Zeller 	case HDA_CODEC_FMT_BITS_16:
64484659b24SMichael Zeller 		params->format = AFMT_S16_LE;
64584659b24SMichael Zeller 		break;
64684659b24SMichael Zeller 	case HDA_CODEC_FMT_BITS_24:
64784659b24SMichael Zeller 		params->format = AFMT_S24_LE;
64884659b24SMichael Zeller 		break;
64984659b24SMichael Zeller 	case HDA_CODEC_FMT_BITS_32:
65084659b24SMichael Zeller 		params->format = AFMT_S32_LE;
65184659b24SMichael Zeller 		break;
65284659b24SMichael Zeller 	default:
653154972afSPatrick Mooney 		DPRINTF("Unknown format bits: 0x%x",
65484659b24SMichael Zeller 		    fmt & HDA_CODEC_FMT_BITS_MASK);
65584659b24SMichael Zeller 		return (-1);
65684659b24SMichael Zeller 	}
65784659b24SMichael Zeller 
65884659b24SMichael Zeller 	/* Compute the Number of Channels */
65984659b24SMichael Zeller 	params->channels = (fmt & HDA_CODEC_FMT_CHAN_MASK) + 1;
66084659b24SMichael Zeller 
66184659b24SMichael Zeller 	return (0);
66284659b24SMichael Zeller }
66384659b24SMichael Zeller 
66484659b24SMichael Zeller static uint32_t
hda_codec_audio_output_nid(struct hda_codec_softc * sc,uint16_t verb,uint16_t payload)66584659b24SMichael Zeller hda_codec_audio_output_nid(struct hda_codec_softc *sc, uint16_t verb,
66684659b24SMichael Zeller     uint16_t payload)
66784659b24SMichael Zeller {
66884659b24SMichael Zeller 	struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
66984659b24SMichael Zeller 	int res;
67084659b24SMichael Zeller 
67184659b24SMichael Zeller 	res = hda_codec_audio_inout_nid(st, verb, payload);
67284659b24SMichael Zeller 
67384659b24SMichael Zeller 	return (res);
67484659b24SMichael Zeller }
67584659b24SMichael Zeller 
67684659b24SMichael Zeller static void
hda_codec_audio_output_do_transfer(void * arg)67784659b24SMichael Zeller hda_codec_audio_output_do_transfer(void *arg)
67884659b24SMichael Zeller {
67959d65d31SAndy Fiddaman 	const struct hda_ops *hops = NULL;
68084659b24SMichael Zeller 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
68184659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
68284659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
68384659b24SMichael Zeller 	struct audio *aud = NULL;
68484659b24SMichael Zeller 	int err;
68584659b24SMichael Zeller 
68684659b24SMichael Zeller 	hci = sc->hci;
68784659b24SMichael Zeller 	assert(hci);
68884659b24SMichael Zeller 
68984659b24SMichael Zeller 	hops = hci->hops;
69084659b24SMichael Zeller 	assert(hops);
69184659b24SMichael Zeller 
69284659b24SMichael Zeller 	st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
69384659b24SMichael Zeller 	aud = st->aud;
69484659b24SMichael Zeller 
69584659b24SMichael Zeller 	err = hops->transfer(hci, st->stream, 1, st->buf, sizeof(st->buf));
69684659b24SMichael Zeller 	if (err)
69784659b24SMichael Zeller 		return;
69884659b24SMichael Zeller 
69984659b24SMichael Zeller 	err = audio_playback(aud, st->buf, sizeof(st->buf));
70084659b24SMichael Zeller 	assert(!err);
70184659b24SMichael Zeller }
70284659b24SMichael Zeller 
70384659b24SMichael Zeller static int
hda_codec_audio_output_do_setup(void * arg)70484659b24SMichael Zeller hda_codec_audio_output_do_setup(void *arg)
70584659b24SMichael Zeller {
70684659b24SMichael Zeller 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
70784659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
70884659b24SMichael Zeller 	struct audio *aud = NULL;
70984659b24SMichael Zeller 	struct audio_params params;
71084659b24SMichael Zeller 	int err;
71184659b24SMichael Zeller 
71284659b24SMichael Zeller 	st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
71384659b24SMichael Zeller 	aud = st->aud;
71484659b24SMichael Zeller 
71584659b24SMichael Zeller 	err = hda_codec_parse_format(st->fmt, &params);
71684659b24SMichael Zeller 	if (err)
71784659b24SMichael Zeller 		return (-1);
71884659b24SMichael Zeller 
719154972afSPatrick Mooney 	DPRINTF("rate: %d, channels: %d, format: 0x%x",
72084659b24SMichael Zeller 	    params.rate, params.channels, params.format);
72184659b24SMichael Zeller 
72284659b24SMichael Zeller 	return (audio_set_params(aud, &params));
72384659b24SMichael Zeller }
72484659b24SMichael Zeller 
72584659b24SMichael Zeller static uint32_t
hda_codec_audio_input_nid(struct hda_codec_softc * sc,uint16_t verb,uint16_t payload)72684659b24SMichael Zeller hda_codec_audio_input_nid(struct hda_codec_softc *sc, uint16_t verb,
72784659b24SMichael Zeller     uint16_t payload)
72884659b24SMichael Zeller {
72984659b24SMichael Zeller 	struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_INPUT];
73084659b24SMichael Zeller 	int res;
73184659b24SMichael Zeller 
73284659b24SMichael Zeller 	res = hda_codec_audio_inout_nid(st, verb, payload);
73384659b24SMichael Zeller 
73484659b24SMichael Zeller 	return (res);
73584659b24SMichael Zeller }
73684659b24SMichael Zeller 
73784659b24SMichael Zeller static void
hda_codec_audio_input_do_transfer(void * arg)73884659b24SMichael Zeller hda_codec_audio_input_do_transfer(void *arg)
73984659b24SMichael Zeller {
74059d65d31SAndy Fiddaman 	const struct hda_ops *hops = NULL;
74184659b24SMichael Zeller 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
74284659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
74384659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
74484659b24SMichael Zeller 	struct audio *aud = NULL;
74584659b24SMichael Zeller 	int err;
74684659b24SMichael Zeller 
74784659b24SMichael Zeller 	hci = sc->hci;
74884659b24SMichael Zeller 	assert(hci);
74984659b24SMichael Zeller 
75084659b24SMichael Zeller 	hops = hci->hops;
75184659b24SMichael Zeller 	assert(hops);
75284659b24SMichael Zeller 
75384659b24SMichael Zeller 	st = &sc->streams[HDA_CODEC_STREAM_INPUT];
75484659b24SMichael Zeller 	aud = st->aud;
75584659b24SMichael Zeller 
75684659b24SMichael Zeller 	err = audio_record(aud, st->buf, sizeof(st->buf));
75784659b24SMichael Zeller 	assert(!err);
75884659b24SMichael Zeller 
75984659b24SMichael Zeller 	hops->transfer(hci, st->stream, 0, st->buf, sizeof(st->buf));
76084659b24SMichael Zeller }
76184659b24SMichael Zeller 
76284659b24SMichael Zeller static int
hda_codec_audio_input_do_setup(void * arg)76384659b24SMichael Zeller hda_codec_audio_input_do_setup(void *arg)
76484659b24SMichael Zeller {
76584659b24SMichael Zeller 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
76684659b24SMichael Zeller 	struct hda_codec_stream *st = NULL;
76784659b24SMichael Zeller 	struct audio *aud = NULL;
76884659b24SMichael Zeller 	struct audio_params params;
76984659b24SMichael Zeller 	int err;
77084659b24SMichael Zeller 
77184659b24SMichael Zeller 	st = &sc->streams[HDA_CODEC_STREAM_INPUT];
77284659b24SMichael Zeller 	aud = st->aud;
77384659b24SMichael Zeller 
77484659b24SMichael Zeller 	err = hda_codec_parse_format(st->fmt, &params);
77584659b24SMichael Zeller 	if (err)
77684659b24SMichael Zeller 		return (-1);
77784659b24SMichael Zeller 
778154972afSPatrick Mooney 	DPRINTF("rate: %d, channels: %d, format: 0x%x",
77984659b24SMichael Zeller 	    params.rate, params.channels, params.format);
78084659b24SMichael Zeller 
78184659b24SMichael Zeller 	return (audio_set_params(aud, &params));
78284659b24SMichael Zeller }
78384659b24SMichael Zeller 
78484659b24SMichael Zeller static uint32_t
hda_codec_audio_inout_nid(struct hda_codec_stream * st,uint16_t verb,uint16_t payload)78584659b24SMichael Zeller hda_codec_audio_inout_nid(struct hda_codec_stream *st, uint16_t verb,
78684659b24SMichael Zeller     uint16_t payload)
78784659b24SMichael Zeller {
78884659b24SMichael Zeller 	uint32_t res = 0;
78984659b24SMichael Zeller 	uint8_t mute = 0;
79084659b24SMichael Zeller 	uint8_t gain = 0;
79184659b24SMichael Zeller 
792154972afSPatrick Mooney 	DPRINTF("%s verb: 0x%x, payload, 0x%x", st->actx.name, verb, payload);
79384659b24SMichael Zeller 
79484659b24SMichael Zeller 	switch (verb) {
79584659b24SMichael Zeller 	case HDA_CMD_VERB_GET_CONV_FMT:
79684659b24SMichael Zeller 		res = st->fmt;
79784659b24SMichael Zeller 		break;
79884659b24SMichael Zeller 	case HDA_CMD_VERB_SET_CONV_FMT:
79984659b24SMichael Zeller 		st->fmt = payload;
80084659b24SMichael Zeller 		break;
80184659b24SMichael Zeller 	case HDA_CMD_VERB_GET_AMP_GAIN_MUTE:
80284659b24SMichael Zeller 		if (payload & HDA_CMD_GET_AMP_GAIN_MUTE_LEFT) {
80384659b24SMichael Zeller 			res = st->left_gain | st->left_mute;
804154972afSPatrick Mooney 			DPRINTF("GET_AMP_GAIN_MUTE_LEFT: 0x%x", res);
80584659b24SMichael Zeller 		} else {
80684659b24SMichael Zeller 			res = st->right_gain | st->right_mute;
807154972afSPatrick Mooney 			DPRINTF("GET_AMP_GAIN_MUTE_RIGHT: 0x%x", res);
80884659b24SMichael Zeller 		}
80984659b24SMichael Zeller 		break;
81084659b24SMichael Zeller 	case HDA_CMD_VERB_SET_AMP_GAIN_MUTE:
81184659b24SMichael Zeller 		mute = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
81284659b24SMichael Zeller 		gain = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK;
81384659b24SMichael Zeller 
81484659b24SMichael Zeller 		if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_LEFT) {
81584659b24SMichael Zeller 			st->left_mute = mute;
81684659b24SMichael Zeller 			st->left_gain = gain;
81784659b24SMichael Zeller 			DPRINTF("SET_AMP_GAIN_MUTE_LEFT: \
818154972afSPatrick Mooney 			    mute: 0x%x gain: 0x%x", mute, gain);
81984659b24SMichael Zeller 		}
82084659b24SMichael Zeller 
82184659b24SMichael Zeller 		if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT) {
82284659b24SMichael Zeller 			st->right_mute = mute;
82384659b24SMichael Zeller 			st->right_gain = gain;
82484659b24SMichael Zeller 			DPRINTF("SET_AMP_GAIN_MUTE_RIGHT: \
825154972afSPatrick Mooney 			    mute: 0x%x gain: 0x%x", mute, gain);
82684659b24SMichael Zeller 		}
82784659b24SMichael Zeller 		break;
82884659b24SMichael Zeller 	case HDA_CMD_VERB_GET_CONV_STREAM_CHAN:
82984659b24SMichael Zeller 		res = (st->stream << 4) | st->channel;
83084659b24SMichael Zeller 		break;
83184659b24SMichael Zeller 	case HDA_CMD_VERB_SET_CONV_STREAM_CHAN:
83284659b24SMichael Zeller 		st->channel = payload & 0x0f;
83384659b24SMichael Zeller 		st->stream = (payload >> 4) & 0x0f;
834154972afSPatrick Mooney 		DPRINTF("st->channel: 0x%x st->stream: 0x%x",
83584659b24SMichael Zeller 		    st->channel, st->stream);
83684659b24SMichael Zeller 		if (!st->stream)
83784659b24SMichael Zeller 			hda_audio_ctxt_stop(&st->actx);
83884659b24SMichael Zeller 		break;
83984659b24SMichael Zeller 	default:
840154972afSPatrick Mooney 		DPRINTF("Unknown VERB: 0x%x", verb);
84184659b24SMichael Zeller 		break;
84284659b24SMichael Zeller 	}
84384659b24SMichael Zeller 
84484659b24SMichael Zeller 	return (res);
84584659b24SMichael Zeller }
84684659b24SMichael Zeller 
8474f3f3e9aSAndy Fiddaman static const struct hda_codec_class hda_codec = {
84884659b24SMichael Zeller 	.name		= "hda_codec",
84984659b24SMichael Zeller 	.init		= hda_codec_init,
85084659b24SMichael Zeller 	.reset		= hda_codec_reset,
85184659b24SMichael Zeller 	.command	= hda_codec_command,
85284659b24SMichael Zeller 	.notify		= hda_codec_notify,
85384659b24SMichael Zeller };
85484659b24SMichael Zeller HDA_EMUL_SET(hda_codec);
85584659b24SMichael Zeller 
85684659b24SMichael Zeller /*
85784659b24SMichael Zeller  * HDA Audio Context module function definitions
85884659b24SMichael Zeller  */
85984659b24SMichael Zeller 
86084659b24SMichael Zeller static void *
hda_audio_ctxt_thr(void * arg)86184659b24SMichael Zeller hda_audio_ctxt_thr(void *arg)
86284659b24SMichael Zeller {
86384659b24SMichael Zeller 	struct hda_audio_ctxt *actx = arg;
86484659b24SMichael Zeller 
865154972afSPatrick Mooney 	DPRINTF("Start Thread: %s", actx->name);
86684659b24SMichael Zeller 
86784659b24SMichael Zeller 	pthread_mutex_lock(&actx->mtx);
86884659b24SMichael Zeller 	while (1) {
86984659b24SMichael Zeller 		while (!actx->run)
87084659b24SMichael Zeller 			pthread_cond_wait(&actx->cond, &actx->mtx);
87184659b24SMichael Zeller 
87284659b24SMichael Zeller 		actx->do_transfer(actx->priv);
87384659b24SMichael Zeller 	}
87484659b24SMichael Zeller 	pthread_mutex_unlock(&actx->mtx);
87584659b24SMichael Zeller 
87684659b24SMichael Zeller 	pthread_exit(NULL);
87784659b24SMichael Zeller 	return (NULL);
87884659b24SMichael Zeller }
87984659b24SMichael Zeller 
88084659b24SMichael Zeller static int
hda_audio_ctxt_init(struct hda_audio_ctxt * actx,const char * tname,transfer_func_t do_transfer,setup_func_t do_setup,void * priv)88184659b24SMichael Zeller hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
88284659b24SMichael Zeller     transfer_func_t do_transfer, setup_func_t do_setup, void *priv)
88384659b24SMichael Zeller {
88484659b24SMichael Zeller 	int err;
88584659b24SMichael Zeller 
88684659b24SMichael Zeller 	assert(actx);
88784659b24SMichael Zeller 	assert(tname);
88884659b24SMichael Zeller 	assert(do_transfer);
88984659b24SMichael Zeller 	assert(do_setup);
89084659b24SMichael Zeller 	assert(priv);
89184659b24SMichael Zeller 
89284659b24SMichael Zeller 	memset(actx, 0, sizeof(*actx));
89384659b24SMichael Zeller 
89484659b24SMichael Zeller 	actx->run = 0;
89584659b24SMichael Zeller 	actx->do_transfer = do_transfer;
89684659b24SMichael Zeller 	actx->do_setup = do_setup;
89784659b24SMichael Zeller 	actx->priv = priv;
89884659b24SMichael Zeller 	if (strlen(tname) < sizeof(actx->name))
89984659b24SMichael Zeller 		memcpy(actx->name, tname, strlen(tname) + 1);
90084659b24SMichael Zeller 	else
90184659b24SMichael Zeller 		strcpy(actx->name, "unknown");
90284659b24SMichael Zeller 
90384659b24SMichael Zeller 	err = pthread_mutex_init(&actx->mtx, NULL);
90484659b24SMichael Zeller 	assert(!err);
90584659b24SMichael Zeller 
90684659b24SMichael Zeller 	err = pthread_cond_init(&actx->cond, NULL);
90784659b24SMichael Zeller 	assert(!err);
90884659b24SMichael Zeller 
90984659b24SMichael Zeller 	err = pthread_create(&actx->tid, NULL, hda_audio_ctxt_thr, actx);
91084659b24SMichael Zeller 	assert(!err);
91184659b24SMichael Zeller 
91284659b24SMichael Zeller 	pthread_set_name_np(actx->tid, tname);
91384659b24SMichael Zeller 
91484659b24SMichael Zeller 	actx->started = 1;
91584659b24SMichael Zeller 
91684659b24SMichael Zeller 	return (0);
91784659b24SMichael Zeller }
91884659b24SMichael Zeller 
91984659b24SMichael Zeller static int
hda_audio_ctxt_start(struct hda_audio_ctxt * actx)92084659b24SMichael Zeller hda_audio_ctxt_start(struct hda_audio_ctxt *actx)
92184659b24SMichael Zeller {
92284659b24SMichael Zeller 	int err = 0;
92384659b24SMichael Zeller 
92484659b24SMichael Zeller 	assert(actx);
92584659b24SMichael Zeller 	assert(actx->started);
92684659b24SMichael Zeller 
92784659b24SMichael Zeller 	/* The stream is supposed to be stopped */
92884659b24SMichael Zeller 	if (actx->run)
92984659b24SMichael Zeller 		return (-1);
93084659b24SMichael Zeller 
93184659b24SMichael Zeller 	pthread_mutex_lock(&actx->mtx);
93284659b24SMichael Zeller 	err = (* actx->do_setup)(actx->priv);
93384659b24SMichael Zeller 	if (!err) {
93484659b24SMichael Zeller 		actx->run = 1;
93584659b24SMichael Zeller 		pthread_cond_signal(&actx->cond);
93684659b24SMichael Zeller 	}
93784659b24SMichael Zeller 	pthread_mutex_unlock(&actx->mtx);
93884659b24SMichael Zeller 
93984659b24SMichael Zeller 	return (err);
94084659b24SMichael Zeller }
94184659b24SMichael Zeller 
94284659b24SMichael Zeller static int
hda_audio_ctxt_stop(struct hda_audio_ctxt * actx)94384659b24SMichael Zeller hda_audio_ctxt_stop(struct hda_audio_ctxt *actx)
94484659b24SMichael Zeller {
94584659b24SMichael Zeller 	actx->run = 0;
94684659b24SMichael Zeller 	return (0);
94784659b24SMichael Zeller }
948