xref: /freebsd/usr.sbin/bhyve/hda_codec.c (revision 37045dfa891a75455ac19ddfb05cda2040343b4e)
16b021cc2SWarner Losh /*-
23e21da8aSMarcelo Araujo  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
33e21da8aSMarcelo Araujo  *
46b021cc2SWarner Losh  * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
56b021cc2SWarner Losh  * All rights reserved.
66b021cc2SWarner Losh  *
76b021cc2SWarner Losh  * Redistribution and use in source and binary forms, with or without
86b021cc2SWarner Losh  * modification, are permitted provided that the following conditions
96b021cc2SWarner Losh  * are met:
106b021cc2SWarner Losh  * 1. Redistributions of source code must retain the above copyright
116b021cc2SWarner Losh  *    notice, this list of conditions and the following disclaimer.
126b021cc2SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
136b021cc2SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
146b021cc2SWarner Losh  *    documentation and/or other materials provided with the distribution.
156b021cc2SWarner Losh  *
166b021cc2SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
176b021cc2SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186b021cc2SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196b021cc2SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206b021cc2SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216b021cc2SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226b021cc2SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236b021cc2SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246b021cc2SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256b021cc2SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266b021cc2SWarner Losh  * SUCH DAMAGE.
276b021cc2SWarner Losh  *
286b021cc2SWarner Losh  */
296b021cc2SWarner Losh 
306b021cc2SWarner Losh #include <sys/cdefs.h>
316b021cc2SWarner Losh __FBSDID("$FreeBSD$");
326b021cc2SWarner Losh 
336b021cc2SWarner Losh #include <pthread.h>
346b021cc2SWarner Losh #include <pthread_np.h>
356b021cc2SWarner Losh #include <unistd.h>
366b021cc2SWarner Losh 
376b021cc2SWarner Losh #include "pci_hda.h"
386b021cc2SWarner Losh #include "audio.h"
396b021cc2SWarner Losh 
406b021cc2SWarner Losh /*
416b021cc2SWarner Losh  * HDA Codec defines
426b021cc2SWarner Losh  */
436b021cc2SWarner Losh #define INTEL_VENDORID				0x8086
446b021cc2SWarner Losh 
456b021cc2SWarner Losh #define HDA_CODEC_SUBSYSTEM_ID			((INTEL_VENDORID << 16) | 0x01)
466b021cc2SWarner Losh #define HDA_CODEC_ROOT_NID			0x00
476b021cc2SWarner Losh #define HDA_CODEC_FG_NID			0x01
486b021cc2SWarner Losh #define HDA_CODEC_AUDIO_OUTPUT_NID		0x02
496b021cc2SWarner Losh #define HDA_CODEC_PIN_OUTPUT_NID		0x03
506b021cc2SWarner Losh #define HDA_CODEC_AUDIO_INPUT_NID		0x04
516b021cc2SWarner Losh #define HDA_CODEC_PIN_INPUT_NID			0x05
526b021cc2SWarner Losh 
536b021cc2SWarner Losh #define HDA_CODEC_STREAMS_COUNT			0x02
546b021cc2SWarner Losh #define HDA_CODEC_STREAM_OUTPUT			0x00
556b021cc2SWarner Losh #define HDA_CODEC_STREAM_INPUT			0x01
566b021cc2SWarner Losh 
576b021cc2SWarner Losh #define HDA_CODEC_PARAMS_COUNT			0x14
586b021cc2SWarner Losh #define HDA_CODEC_CONN_LIST_COUNT		0x01
596b021cc2SWarner Losh #define HDA_CODEC_RESPONSE_EX_UNSOL		0x10
606b021cc2SWarner Losh #define HDA_CODEC_RESPONSE_EX_SOL		0x00
616b021cc2SWarner Losh #define HDA_CODEC_AMP_NUMSTEPS			0x4a
626b021cc2SWarner Losh 
636b021cc2SWarner Losh #define HDA_CODEC_SUPP_STREAM_FORMATS_PCM				\
646b021cc2SWarner Losh 	(1 << HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT)
656b021cc2SWarner Losh 
666b021cc2SWarner Losh #define HDA_CODEC_FMT_BASE_MASK			(0x01 << 14)
676b021cc2SWarner Losh 
686b021cc2SWarner Losh #define HDA_CODEC_FMT_MULT_MASK			(0x07 << 11)
696b021cc2SWarner Losh #define HDA_CODEC_FMT_MULT_2			(0x01 << 11)
706b021cc2SWarner Losh #define HDA_CODEC_FMT_MULT_3			(0x02 << 11)
716b021cc2SWarner Losh #define HDA_CODEC_FMT_MULT_4			(0x03 << 11)
726b021cc2SWarner Losh 
736b021cc2SWarner Losh #define HDA_CODEC_FMT_DIV_MASK			0x07
746b021cc2SWarner Losh #define HDA_CODEC_FMT_DIV_SHIFT			8
756b021cc2SWarner Losh 
766b021cc2SWarner Losh #define HDA_CODEC_FMT_BITS_MASK			(0x07 << 4)
776b021cc2SWarner Losh #define HDA_CODEC_FMT_BITS_8			(0x00 << 4)
786b021cc2SWarner Losh #define HDA_CODEC_FMT_BITS_16			(0x01 << 4)
796b021cc2SWarner Losh #define HDA_CODEC_FMT_BITS_24			(0x03 << 4)
806b021cc2SWarner Losh #define HDA_CODEC_FMT_BITS_32			(0x04 << 4)
816b021cc2SWarner Losh 
826b021cc2SWarner Losh #define HDA_CODEC_FMT_CHAN_MASK			(0x0f << 0)
836b021cc2SWarner Losh 
846b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_OUTPUT					\
856b021cc2SWarner Losh 	(0x00 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
866b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_INPUT					\
876b021cc2SWarner Losh 	(0x01 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
886b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_PIN					\
896b021cc2SWarner Losh 	(0x04 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT)
906b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_CONN_LIST					\
916b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT)
926b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_FORMAT_OVR					\
936b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT)
946b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_AMP_OVR					\
956b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT)
966b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_OUT_AMP					\
976b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT)
986b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_IN_AMP					\
996b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT)
1006b021cc2SWarner Losh #define HDA_CODEC_AUDIO_WCAP_STEREO					\
1016b021cc2SWarner Losh 	(1 << HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT)
1026b021cc2SWarner Losh 
1036b021cc2SWarner Losh #define HDA_CODEC_PIN_CAP_OUTPUT					\
1046b021cc2SWarner Losh 	(1 << HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT)
1056b021cc2SWarner Losh #define HDA_CODEC_PIN_CAP_INPUT						\
1066b021cc2SWarner Losh 	(1 << HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT)
1076b021cc2SWarner Losh #define HDA_CODEC_PIN_CAP_PRESENCE_DETECT				\
1086b021cc2SWarner Losh 	(1 << HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT)
1096b021cc2SWarner Losh 
1106b021cc2SWarner Losh #define HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP				\
1116b021cc2SWarner Losh 	(1 << HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT)
1126b021cc2SWarner Losh #define HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE				\
1136b021cc2SWarner Losh 	(0x03 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT)
1146b021cc2SWarner Losh #define HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS				\
1156b021cc2SWarner Losh 	(HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT)
1166b021cc2SWarner Losh #define HDA_CODEC_OUTPUT_AMP_CAP_OFFSET					\
1176b021cc2SWarner Losh 	(HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT)
1186b021cc2SWarner Losh 
1196b021cc2SWarner Losh #define HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE	0x80
1206b021cc2SWarner Losh #define HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK	0x7f
1216b021cc2SWarner Losh 
1226b021cc2SWarner Losh #define HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED	(1 << 31)
1236b021cc2SWarner Losh #define HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE				\
1246b021cc2SWarner Losh 	(1 << HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT)
1256b021cc2SWarner Losh #define HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE				\
1266b021cc2SWarner Losh 	(1 << HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT)
1276b021cc2SWarner Losh 
1286b021cc2SWarner Losh #define HDA_CONFIG_DEFAULTCONF_COLOR_BLACK				\
1296b021cc2SWarner Losh 	(0x01 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
1306b021cc2SWarner Losh #define HDA_CONFIG_DEFAULTCONF_COLOR_RED				\
1316b021cc2SWarner Losh 	(0x05 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT)
1326b021cc2SWarner Losh 
1336b021cc2SWarner Losh #define HDA_CODEC_BUF_SIZE			HDA_FIFO_SIZE
1346b021cc2SWarner Losh 
1356b021cc2SWarner Losh #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
1366b021cc2SWarner Losh 
1376b021cc2SWarner Losh 
1386b021cc2SWarner Losh /*
1396b021cc2SWarner Losh  * HDA Audio Context data structures
1406b021cc2SWarner Losh  */
1416b021cc2SWarner Losh 
1426b021cc2SWarner Losh typedef void (*transfer_func_t)(void *arg);
1436b021cc2SWarner Losh typedef int (*setup_func_t)(void *arg);
1446b021cc2SWarner Losh 
1456b021cc2SWarner Losh struct hda_audio_ctxt {
1466b021cc2SWarner Losh 	char name[64];
1476b021cc2SWarner Losh 	uint8_t run;
1486b021cc2SWarner Losh 	uint8_t started;
1496b021cc2SWarner Losh 	void *priv;
1506b021cc2SWarner Losh 	pthread_t tid;
1516b021cc2SWarner Losh 	pthread_mutex_t mtx;
1526b021cc2SWarner Losh 	pthread_cond_t cond;
1536b021cc2SWarner Losh 	setup_func_t do_setup;
1546b021cc2SWarner Losh 	transfer_func_t do_transfer;
1556b021cc2SWarner Losh };
1566b021cc2SWarner Losh 
1576b021cc2SWarner Losh /*
1586b021cc2SWarner Losh  * HDA Audio Context module function declarations
1596b021cc2SWarner Losh  */
1606b021cc2SWarner Losh 
1616b021cc2SWarner Losh static void *hda_audio_ctxt_thr(void *arg);
1626b021cc2SWarner Losh static int hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
1636b021cc2SWarner Losh     transfer_func_t do_transfer, setup_func_t do_setup, void *priv);
1646b021cc2SWarner Losh static int hda_audio_ctxt_start(struct hda_audio_ctxt *actx);
1656b021cc2SWarner Losh static int hda_audio_ctxt_stop(struct hda_audio_ctxt *actx);
1666b021cc2SWarner Losh 
1676b021cc2SWarner Losh /*
1686b021cc2SWarner Losh  * HDA Codec data structures
1696b021cc2SWarner Losh  */
1706b021cc2SWarner Losh 
1716b021cc2SWarner Losh struct hda_codec_softc;
1726b021cc2SWarner Losh 
1736b021cc2SWarner Losh typedef uint32_t (*verb_func_t)(struct hda_codec_softc *sc, uint16_t verb,
1746b021cc2SWarner Losh 				    uint16_t payload);
1756b021cc2SWarner Losh 
1766b021cc2SWarner Losh struct hda_codec_stream {
1776b021cc2SWarner Losh 	uint8_t buf[HDA_CODEC_BUF_SIZE];
1786b021cc2SWarner Losh 	uint8_t channel;
1796b021cc2SWarner Losh 	uint16_t fmt;
1806b021cc2SWarner Losh 	uint8_t stream;
1816b021cc2SWarner Losh 
1826b021cc2SWarner Losh 	uint8_t left_gain;
1836b021cc2SWarner Losh 	uint8_t right_gain;
1846b021cc2SWarner Losh 	uint8_t left_mute;
1856b021cc2SWarner Losh 	uint8_t right_mute;
1866b021cc2SWarner Losh 
1876b021cc2SWarner Losh 	struct audio *aud;
1886b021cc2SWarner Losh 	struct hda_audio_ctxt actx;
1896b021cc2SWarner Losh };
1906b021cc2SWarner Losh 
1916b021cc2SWarner Losh struct hda_codec_softc {
1926b021cc2SWarner Losh 	uint32_t no_nodes;
1936b021cc2SWarner Losh 	uint32_t subsystem_id;
1946b021cc2SWarner Losh 	const uint32_t (*get_parameters)[HDA_CODEC_PARAMS_COUNT];
1956b021cc2SWarner Losh 	const uint8_t (*conn_list)[HDA_CODEC_CONN_LIST_COUNT];
1966b021cc2SWarner Losh 	const uint32_t *conf_default;
1976b021cc2SWarner Losh 	const uint8_t *pin_ctrl_default;
1986b021cc2SWarner Losh 	const verb_func_t *verb_handlers;
1996b021cc2SWarner Losh 
2006b021cc2SWarner Losh 	struct hda_codec_inst *hci;
2016b021cc2SWarner Losh 	struct hda_codec_stream streams[HDA_CODEC_STREAMS_COUNT];
2026b021cc2SWarner Losh };
2036b021cc2SWarner Losh 
2046b021cc2SWarner Losh /*
2056b021cc2SWarner Losh  * HDA Codec module function declarations
2066b021cc2SWarner Losh  */
2076b021cc2SWarner Losh static int hda_codec_init(struct hda_codec_inst *hci, const char *play,
208621b5090SJohn Baldwin     const char *rec);
2096b021cc2SWarner Losh static int hda_codec_reset(struct hda_codec_inst *hci);
2106b021cc2SWarner Losh static int hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data);
2116b021cc2SWarner Losh static int hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
2126b021cc2SWarner Losh     uint8_t stream, uint8_t dir);
2136b021cc2SWarner Losh 
2146b021cc2SWarner Losh static int hda_codec_parse_format(uint16_t fmt, struct audio_params *params);
2156b021cc2SWarner Losh 
2166b021cc2SWarner Losh static uint32_t hda_codec_audio_output_nid(struct hda_codec_softc *sc,
2176b021cc2SWarner Losh     uint16_t verb, uint16_t payload);
2186b021cc2SWarner Losh static void hda_codec_audio_output_do_transfer(void *arg);
2196b021cc2SWarner Losh static int hda_codec_audio_output_do_setup(void *arg);
2206b021cc2SWarner Losh static uint32_t hda_codec_audio_input_nid(struct hda_codec_softc *sc,
2216b021cc2SWarner Losh     uint16_t verb, uint16_t payload);
2226b021cc2SWarner Losh static void hda_codec_audio_input_do_transfer(void *arg);
2236b021cc2SWarner Losh static int hda_codec_audio_input_do_setup(void *arg);
2246b021cc2SWarner Losh 
2256b021cc2SWarner Losh static uint32_t hda_codec_audio_inout_nid(struct hda_codec_stream *st,
2266b021cc2SWarner Losh     uint16_t verb, uint16_t payload);
2276b021cc2SWarner Losh 
2286b021cc2SWarner Losh /*
2296b021cc2SWarner Losh  * HDA Codec global data
2306b021cc2SWarner Losh  */
2316b021cc2SWarner Losh 
2326b021cc2SWarner Losh #define HDA_CODEC_ROOT_DESC						\
2336b021cc2SWarner Losh 	[HDA_CODEC_ROOT_NID] = {					\
2346b021cc2SWarner Losh 		[HDA_PARAM_VENDOR_ID] = INTEL_VENDORID,			\
2356b021cc2SWarner Losh 		[HDA_PARAM_REVISION_ID] = 0xffff,			\
2366b021cc2SWarner Losh 		/* 1 Subnode, StartNid = 1 */				\
2376b021cc2SWarner Losh 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00010001,		\
2386b021cc2SWarner Losh 	},								\
2396b021cc2SWarner Losh 
2406b021cc2SWarner Losh #define HDA_CODEC_FG_COMMON_DESC					\
2416b021cc2SWarner Losh 	[HDA_PARAM_FCT_GRP_TYPE] = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO,\
2426b021cc2SWarner Losh 	/* B8 - B32, 8.0 - 192.0kHz */					\
2436b021cc2SWarner Losh 	[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x1f << 16) | 0x7ff,		\
2446b021cc2SWarner Losh 	[HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM,\
2456b021cc2SWarner Losh 	[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */		\
2466b021cc2SWarner Losh 	[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */		\
2476b021cc2SWarner Losh 	[HDA_PARAM_GPIO_COUNT] = 0x00,					\
2486b021cc2SWarner Losh 
2496b021cc2SWarner Losh #define HDA_CODEC_FG_OUTPUT_DESC					\
2506b021cc2SWarner Losh 	[HDA_CODEC_FG_NID] = {						\
2516b021cc2SWarner Losh 		/* 2 Subnodes, StartNid = 2 */				\
2526b021cc2SWarner Losh 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00020002,		\
2536b021cc2SWarner Losh 		HDA_CODEC_FG_COMMON_DESC				\
2546b021cc2SWarner Losh 	},								\
2556b021cc2SWarner Losh 
2566b021cc2SWarner Losh #define HDA_CODEC_FG_INPUT_DESC						\
2576b021cc2SWarner Losh 	[HDA_CODEC_FG_NID] = {						\
2586b021cc2SWarner Losh 		/* 2 Subnodes, StartNid = 4 */				\
2596b021cc2SWarner Losh 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00040002,		\
2606b021cc2SWarner Losh 		HDA_CODEC_FG_COMMON_DESC				\
2616b021cc2SWarner Losh 	},								\
2626b021cc2SWarner Losh 
2636b021cc2SWarner Losh #define HDA_CODEC_FG_DUPLEX_DESC					\
2646b021cc2SWarner Losh 	[HDA_CODEC_FG_NID] = {						\
2656b021cc2SWarner Losh 		/* 4 Subnodes, StartNid = 2 */				\
2666b021cc2SWarner Losh 		[HDA_PARAM_SUB_NODE_COUNT] = 0x00020004,		\
2676b021cc2SWarner Losh 		HDA_CODEC_FG_COMMON_DESC				\
2686b021cc2SWarner Losh 	},								\
2696b021cc2SWarner Losh 
2706b021cc2SWarner Losh #define HDA_CODEC_OUTPUT_DESC						\
2716b021cc2SWarner Losh 	[HDA_CODEC_AUDIO_OUTPUT_NID] = {				\
2726b021cc2SWarner Losh 		[HDA_PARAM_AUDIO_WIDGET_CAP] = 				\
2736b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_OUTPUT |		\
2746b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_FORMAT_OVR |	\
2756b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_AMP_OVR |		\
2766b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_OUT_AMP |		\
2776b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
2786b021cc2SWarner Losh 		/* B16, 16.0 - 192.0kHz */				\
2796b021cc2SWarner Losh 		[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc,	\
2806b021cc2SWarner Losh 		[HDA_PARAM_SUPP_STREAM_FORMATS] =			\
2816b021cc2SWarner Losh 				HDA_CODEC_SUPP_STREAM_FORMATS_PCM,	\
2826b021cc2SWarner Losh 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
2836b021cc2SWarner Losh 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x00,			\
2846b021cc2SWarner Losh 		[HDA_PARAM_OUTPUT_AMP_CAP] =				\
2856b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP |	\
2866b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE |	\
2876b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS |	\
2886b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_OFFSET,	\
2896b021cc2SWarner Losh 	},								\
2906b021cc2SWarner Losh 	[HDA_CODEC_PIN_OUTPUT_NID] = {					\
2916b021cc2SWarner Losh 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
2926b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_PIN |		\
2936b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_CONN_LIST |	\
2946b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
2956b021cc2SWarner Losh 		[HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_OUTPUT |	\
2966b021cc2SWarner Losh 				      HDA_CODEC_PIN_CAP_PRESENCE_DETECT,\
2976b021cc2SWarner Losh 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
2986b021cc2SWarner Losh 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x01,			\
2996b021cc2SWarner Losh 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
3006b021cc2SWarner Losh 	},								\
3016b021cc2SWarner Losh 
3026b021cc2SWarner Losh #define HDA_CODEC_INPUT_DESC						\
3036b021cc2SWarner Losh 	[HDA_CODEC_AUDIO_INPUT_NID] = {					\
3046b021cc2SWarner Losh 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
3056b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_INPUT |		\
3066b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_CONN_LIST |	\
3076b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_FORMAT_OVR |	\
3086b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_AMP_OVR |		\
3096b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_IN_AMP |		\
3106b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
3116b021cc2SWarner Losh 		/* B16, 16.0 - 192.0kHz */				\
3126b021cc2SWarner Losh 		[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc,	\
3136b021cc2SWarner Losh 		[HDA_PARAM_SUPP_STREAM_FORMATS] =			\
3146b021cc2SWarner Losh 				HDA_CODEC_SUPP_STREAM_FORMATS_PCM,	\
3156b021cc2SWarner Losh 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
3166b021cc2SWarner Losh 		[HDA_PARAM_CONN_LIST_LENGTH] = 0x01,			\
3176b021cc2SWarner Losh 		[HDA_PARAM_INPUT_AMP_CAP] =				\
3186b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP |	\
3196b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE |	\
3206b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS |	\
3216b021cc2SWarner Losh 				HDA_CODEC_OUTPUT_AMP_CAP_OFFSET,	\
3226b021cc2SWarner Losh 	},								\
3236b021cc2SWarner Losh 	[HDA_CODEC_PIN_INPUT_NID] = {					\
3246b021cc2SWarner Losh 		[HDA_PARAM_AUDIO_WIDGET_CAP] =				\
3256b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_PIN |		\
3266b021cc2SWarner Losh 				HDA_CODEC_AUDIO_WCAP_STEREO,		\
3276b021cc2SWarner Losh 		[HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_INPUT |		\
3286b021cc2SWarner Losh 				HDA_CODEC_PIN_CAP_PRESENCE_DETECT,	\
3296b021cc2SWarner Losh 		[HDA_PARAM_INPUT_AMP_CAP] = 0x00,	/* None */	\
3306b021cc2SWarner Losh 		[HDA_PARAM_OUTPUT_AMP_CAP] = 0x00,	/* None */	\
3316b021cc2SWarner Losh 	},								\
3326b021cc2SWarner Losh 
3336b021cc2SWarner Losh static const uint32_t
3346b021cc2SWarner Losh hda_codec_output_parameters[][HDA_CODEC_PARAMS_COUNT] = {
3356b021cc2SWarner Losh 	HDA_CODEC_ROOT_DESC
3366b021cc2SWarner Losh 	HDA_CODEC_FG_OUTPUT_DESC
3376b021cc2SWarner Losh 	HDA_CODEC_OUTPUT_DESC
3386b021cc2SWarner Losh };
3396b021cc2SWarner Losh 
3406b021cc2SWarner Losh static const uint32_t
3416b021cc2SWarner Losh hda_codec_input_parameters[][HDA_CODEC_PARAMS_COUNT] = {
3426b021cc2SWarner Losh 	HDA_CODEC_ROOT_DESC
3436b021cc2SWarner Losh 	HDA_CODEC_FG_INPUT_DESC
3446b021cc2SWarner Losh 	HDA_CODEC_INPUT_DESC
3456b021cc2SWarner Losh };
3466b021cc2SWarner Losh 
3476b021cc2SWarner Losh static const uint32_t
3486b021cc2SWarner Losh hda_codec_duplex_parameters[][HDA_CODEC_PARAMS_COUNT] = {
3496b021cc2SWarner Losh 	HDA_CODEC_ROOT_DESC
3506b021cc2SWarner Losh 	HDA_CODEC_FG_DUPLEX_DESC
3516b021cc2SWarner Losh 	HDA_CODEC_OUTPUT_DESC
3526b021cc2SWarner Losh 	HDA_CODEC_INPUT_DESC
3536b021cc2SWarner Losh };
3546b021cc2SWarner Losh 
3556b021cc2SWarner Losh #define HDA_CODEC_NODES_COUNT	(ARRAY_SIZE(hda_codec_duplex_parameters))
3566b021cc2SWarner Losh 
3576b021cc2SWarner Losh static const uint8_t
3586b021cc2SWarner Losh hda_codec_conn_list[HDA_CODEC_NODES_COUNT][HDA_CODEC_CONN_LIST_COUNT] = {
3596b021cc2SWarner Losh 	[HDA_CODEC_PIN_OUTPUT_NID] = {HDA_CODEC_AUDIO_OUTPUT_NID},
3606b021cc2SWarner Losh 	[HDA_CODEC_AUDIO_INPUT_NID] = {HDA_CODEC_PIN_INPUT_NID},
3616b021cc2SWarner Losh };
3626b021cc2SWarner Losh 
3636b021cc2SWarner Losh static const uint32_t
3646b021cc2SWarner Losh hda_codec_conf_default[HDA_CODEC_NODES_COUNT] = {
3656b021cc2SWarner Losh 	[HDA_CODEC_PIN_OUTPUT_NID] =					\
3666b021cc2SWarner Losh 		HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
3676b021cc2SWarner Losh 		HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT |
3686b021cc2SWarner Losh 		HDA_CONFIG_DEFAULTCONF_COLOR_BLACK |
3696b021cc2SWarner Losh 		(0x01 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
3706b021cc2SWarner Losh 	[HDA_CODEC_PIN_INPUT_NID] = HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK |
3716b021cc2SWarner Losh 				    HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN |
3726b021cc2SWarner Losh 				    HDA_CONFIG_DEFAULTCONF_COLOR_RED |
3736b021cc2SWarner Losh 			(0x02 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT),
3746b021cc2SWarner Losh };
3756b021cc2SWarner Losh 
3766b021cc2SWarner Losh static const uint8_t
3776b021cc2SWarner Losh hda_codec_pin_ctrl_default[HDA_CODEC_NODES_COUNT] = {
3786b021cc2SWarner Losh 	[HDA_CODEC_PIN_OUTPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE,
3796b021cc2SWarner Losh 	[HDA_CODEC_PIN_INPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE,
3806b021cc2SWarner Losh };
3816b021cc2SWarner Losh 
3826b021cc2SWarner Losh static const
3836b021cc2SWarner Losh verb_func_t hda_codec_verb_handlers[HDA_CODEC_NODES_COUNT] = {
3846b021cc2SWarner Losh 	[HDA_CODEC_AUDIO_OUTPUT_NID] = hda_codec_audio_output_nid,
3856b021cc2SWarner Losh 	[HDA_CODEC_AUDIO_INPUT_NID] = hda_codec_audio_input_nid,
3866b021cc2SWarner Losh };
3876b021cc2SWarner Losh 
3886b021cc2SWarner Losh /*
3896b021cc2SWarner Losh  * HDA Codec module function definitions
3906b021cc2SWarner Losh  */
3916b021cc2SWarner Losh 
3926b021cc2SWarner Losh static int
3936b021cc2SWarner Losh hda_codec_init(struct hda_codec_inst *hci, const char *play,
394621b5090SJohn Baldwin     const char *rec)
3956b021cc2SWarner Losh {
3966b021cc2SWarner Losh 	struct hda_codec_softc *sc = NULL;
3976b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
3986b021cc2SWarner Losh 	int err;
3996b021cc2SWarner Losh 
4006b021cc2SWarner Losh 	if (!(play || rec))
4016b021cc2SWarner Losh 		return (-1);
4026b021cc2SWarner Losh 
4036b021cc2SWarner Losh 	sc = calloc(1, sizeof(*sc));
4046b021cc2SWarner Losh 	if (!sc)
4056b021cc2SWarner Losh 		return (-1);
4066b021cc2SWarner Losh 
4076b021cc2SWarner Losh 	if (play && rec)
4086b021cc2SWarner Losh 		sc->get_parameters = hda_codec_duplex_parameters;
4096b021cc2SWarner Losh 	else {
4106b021cc2SWarner Losh 		if (play)
4116b021cc2SWarner Losh 			sc->get_parameters = hda_codec_output_parameters;
4126b021cc2SWarner Losh 		else
4136b021cc2SWarner Losh 			sc->get_parameters = hda_codec_input_parameters;
4146b021cc2SWarner Losh 	}
4156b021cc2SWarner Losh 	sc->subsystem_id = HDA_CODEC_SUBSYSTEM_ID;
4166b021cc2SWarner Losh 	sc->no_nodes = HDA_CODEC_NODES_COUNT;
4176b021cc2SWarner Losh 	sc->conn_list = hda_codec_conn_list;
4186b021cc2SWarner Losh 	sc->conf_default = hda_codec_conf_default;
4196b021cc2SWarner Losh 	sc->pin_ctrl_default = hda_codec_pin_ctrl_default;
4206b021cc2SWarner Losh 	sc->verb_handlers = hda_codec_verb_handlers;
421332eff95SVincenzo Maffione 	DPRINTF("HDA Codec nodes: %d", sc->no_nodes);
4226b021cc2SWarner Losh 
4236b021cc2SWarner Losh 	/*
4246b021cc2SWarner Losh 	 * Initialize the Audio Output stream
4256b021cc2SWarner Losh 	 */
4266b021cc2SWarner Losh 	if (play) {
4276b021cc2SWarner Losh 		st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
4286b021cc2SWarner Losh 
4296b021cc2SWarner Losh 		err = hda_audio_ctxt_init(&st->actx, "hda-audio-output",
4306b021cc2SWarner Losh 			hda_codec_audio_output_do_transfer,
4316b021cc2SWarner Losh 			hda_codec_audio_output_do_setup, sc);
4326b021cc2SWarner Losh 		assert(!err);
4336b021cc2SWarner Losh 
4346b021cc2SWarner Losh 		st->aud = audio_init(play, 1);
4356b021cc2SWarner Losh 		if (!st->aud) {
436332eff95SVincenzo Maffione 			DPRINTF("Fail to init the output audio player");
4376b021cc2SWarner Losh 			return (-1);
4386b021cc2SWarner Losh 		}
4396b021cc2SWarner Losh 	}
4406b021cc2SWarner Losh 
4416b021cc2SWarner Losh 	/*
4426b021cc2SWarner Losh 	 * Initialize the Audio Input stream
4436b021cc2SWarner Losh 	 */
4446b021cc2SWarner Losh 	if (rec) {
4456b021cc2SWarner Losh 		st = &sc->streams[HDA_CODEC_STREAM_INPUT];
4466b021cc2SWarner Losh 
4476b021cc2SWarner Losh 		err = hda_audio_ctxt_init(&st->actx, "hda-audio-input",
4486b021cc2SWarner Losh 			hda_codec_audio_input_do_transfer,
4496b021cc2SWarner Losh 			hda_codec_audio_input_do_setup, sc);
4506b021cc2SWarner Losh 		assert(!err);
4516b021cc2SWarner Losh 
4526b021cc2SWarner Losh 		st->aud = audio_init(rec, 0);
4536b021cc2SWarner Losh 		if (!st->aud) {
454332eff95SVincenzo Maffione 			DPRINTF("Fail to init the input audio player");
4556b021cc2SWarner Losh 			return (-1);
4566b021cc2SWarner Losh 		}
4576b021cc2SWarner Losh 	}
4586b021cc2SWarner Losh 
4596b021cc2SWarner Losh 	sc->hci = hci;
4606b021cc2SWarner Losh 	hci->priv = sc;
4616b021cc2SWarner Losh 
4626b021cc2SWarner Losh 	return (0);
4636b021cc2SWarner Losh }
4646b021cc2SWarner Losh 
4656b021cc2SWarner Losh static int
4666b021cc2SWarner Losh hda_codec_reset(struct hda_codec_inst *hci)
4676b021cc2SWarner Losh {
4686b021cc2SWarner Losh 	struct hda_ops *hops = NULL;
4696b021cc2SWarner Losh 	struct hda_codec_softc *sc = NULL;
4706b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
4716b021cc2SWarner Losh 	int i;
4726b021cc2SWarner Losh 
4736b021cc2SWarner Losh 	assert(hci);
4746b021cc2SWarner Losh 
4756b021cc2SWarner Losh 	hops = hci->hops;
4766b021cc2SWarner Losh 	assert(hops);
4776b021cc2SWarner Losh 
4786b021cc2SWarner Losh 	sc = (struct hda_codec_softc *)hci->priv;
4796b021cc2SWarner Losh 	assert(sc);
4806b021cc2SWarner Losh 
4816b021cc2SWarner Losh 	for (i = 0; i < HDA_CODEC_STREAMS_COUNT; i++) {
4826b021cc2SWarner Losh 		st = &sc->streams[i];
4836b021cc2SWarner Losh 		st->left_gain = HDA_CODEC_AMP_NUMSTEPS;
4846b021cc2SWarner Losh 		st->right_gain = HDA_CODEC_AMP_NUMSTEPS;
4856b021cc2SWarner Losh 		st->left_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
4866b021cc2SWarner Losh 		st->right_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
4876b021cc2SWarner Losh 	}
4886b021cc2SWarner Losh 
489332eff95SVincenzo Maffione 	DPRINTF("cad: 0x%x", hci->cad);
4906b021cc2SWarner Losh 
4916b021cc2SWarner Losh 	if (!hops->signal) {
4926b021cc2SWarner Losh 		DPRINTF("The controller ops does not implement \
493332eff95SVincenzo Maffione 			 the signal function");
4946b021cc2SWarner Losh 		return (-1);
4956b021cc2SWarner Losh 	}
4966b021cc2SWarner Losh 
4976b021cc2SWarner Losh 	return (hops->signal(hci));
4986b021cc2SWarner Losh }
4996b021cc2SWarner Losh 
5006b021cc2SWarner Losh static int
5016b021cc2SWarner Losh hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data)
5026b021cc2SWarner Losh {
5036b021cc2SWarner Losh 	struct hda_codec_softc *sc = NULL;
5046b021cc2SWarner Losh 	struct hda_ops *hops = NULL;
5056b021cc2SWarner Losh 	uint8_t cad = 0, nid = 0;
5066b021cc2SWarner Losh 	uint16_t verb = 0, payload = 0;
5076b021cc2SWarner Losh 	uint32_t res = 0;
5086b021cc2SWarner Losh 
5096b021cc2SWarner Losh 	/* 4 bits */
5106b021cc2SWarner Losh 	cad = (cmd_data >> HDA_CMD_CAD_SHIFT) & 0x0f;
5116b021cc2SWarner Losh 	/* 8 bits */
5126b021cc2SWarner Losh 	nid = (cmd_data >> HDA_CMD_NID_SHIFT) & 0xff;
5136b021cc2SWarner Losh 
5146b021cc2SWarner Losh 	if ((cmd_data & 0x70000) == 0x70000) {
5156b021cc2SWarner Losh 		/* 12 bits */
5166b021cc2SWarner Losh 		verb = (cmd_data >> HDA_CMD_VERB_12BIT_SHIFT) & 0x0fff;
5176b021cc2SWarner Losh 		/* 8 bits */
5186b021cc2SWarner Losh 		payload = cmd_data & 0xff;
5196b021cc2SWarner Losh 	} else {
5206b021cc2SWarner Losh 		/* 4 bits */
5216b021cc2SWarner Losh 		verb = (cmd_data >> HDA_CMD_VERB_4BIT_SHIFT) & 0x0f;
5226b021cc2SWarner Losh 		/* 16 bits */
5236b021cc2SWarner Losh 		payload = cmd_data & 0xffff;
5246b021cc2SWarner Losh 	}
5256b021cc2SWarner Losh 
5266b021cc2SWarner Losh 	assert(cad == hci->cad);
5276b021cc2SWarner Losh 	assert(hci);
5286b021cc2SWarner Losh 
5296b021cc2SWarner Losh 	hops = hci->hops;
5306b021cc2SWarner Losh 	assert(hops);
5316b021cc2SWarner Losh 
5326b021cc2SWarner Losh 	sc = (struct hda_codec_softc *)hci->priv;
5336b021cc2SWarner Losh 	assert(sc);
5346b021cc2SWarner Losh 
5356b021cc2SWarner Losh 	assert(nid < sc->no_nodes);
5366b021cc2SWarner Losh 
5376b021cc2SWarner Losh 	if (!hops->response) {
5386b021cc2SWarner Losh 		DPRINTF("The controller ops does not implement \
539332eff95SVincenzo Maffione 			 the response function");
5406b021cc2SWarner Losh 		return (-1);
5416b021cc2SWarner Losh 	}
5426b021cc2SWarner Losh 
5436b021cc2SWarner Losh 	switch (verb) {
5446b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_PARAMETER:
5456b021cc2SWarner Losh 		res = sc->get_parameters[nid][payload];
5466b021cc2SWarner Losh 		break;
5476b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_CONN_LIST_ENTRY:
5486b021cc2SWarner Losh 		res = sc->conn_list[nid][0];
5496b021cc2SWarner Losh 		break;
5506b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_PIN_WIDGET_CTRL:
5516b021cc2SWarner Losh 		res = sc->pin_ctrl_default[nid];
5526b021cc2SWarner Losh 		break;
5536b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_PIN_SENSE:
5546b021cc2SWarner Losh 		res = HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED;
5556b021cc2SWarner Losh 		break;
5566b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT:
5576b021cc2SWarner Losh 		res = sc->conf_default[nid];
5586b021cc2SWarner Losh 		break;
5596b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_SUBSYSTEM_ID:
5606b021cc2SWarner Losh 		res = sc->subsystem_id;
5616b021cc2SWarner Losh 		break;
5626b021cc2SWarner Losh 	default:
5636b021cc2SWarner Losh 		assert(sc->verb_handlers);
5646b021cc2SWarner Losh 		if (sc->verb_handlers[nid])
5656b021cc2SWarner Losh 			res = sc->verb_handlers[nid](sc, verb, payload);
5666b021cc2SWarner Losh 		else
567332eff95SVincenzo Maffione 			DPRINTF("Unknown VERB: 0x%x", verb);
5686b021cc2SWarner Losh 		break;
5696b021cc2SWarner Losh 	}
5706b021cc2SWarner Losh 
571332eff95SVincenzo Maffione 	DPRINTF("cad: 0x%x nid: 0x%x verb: 0x%x payload: 0x%x response: 0x%x",
5726b021cc2SWarner Losh 	    cad, nid, verb, payload, res);
5736b021cc2SWarner Losh 
5746b021cc2SWarner Losh 	return (hops->response(hci, res, HDA_CODEC_RESPONSE_EX_SOL));
5756b021cc2SWarner Losh }
5766b021cc2SWarner Losh 
5776b021cc2SWarner Losh static int
5786b021cc2SWarner Losh hda_codec_notify(struct hda_codec_inst *hci, uint8_t run,
5796b021cc2SWarner Losh     uint8_t stream, uint8_t dir)
5806b021cc2SWarner Losh {
5816b021cc2SWarner Losh 	struct hda_codec_softc *sc = NULL;
5826b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
5836b021cc2SWarner Losh 	struct hda_audio_ctxt *actx = NULL;
5846b021cc2SWarner Losh 	int i;
5856b021cc2SWarner Losh 	int err;
5866b021cc2SWarner Losh 
5876b021cc2SWarner Losh 	assert(hci);
5886b021cc2SWarner Losh 	assert(stream);
5896b021cc2SWarner Losh 
5906b021cc2SWarner Losh 	sc = (struct hda_codec_softc *)hci->priv;
5916b021cc2SWarner Losh 	assert(sc);
5926b021cc2SWarner Losh 
5936b021cc2SWarner Losh 	i = dir ? HDA_CODEC_STREAM_OUTPUT : HDA_CODEC_STREAM_INPUT;
5946b021cc2SWarner Losh 	st = &sc->streams[i];
5956b021cc2SWarner Losh 
596332eff95SVincenzo Maffione 	DPRINTF("run: %d, stream: 0x%x, st->stream: 0x%x dir: %d",
5976b021cc2SWarner Losh 	    run, stream, st->stream, dir);
5986b021cc2SWarner Losh 
5996b021cc2SWarner Losh 	if (stream != st->stream) {
600332eff95SVincenzo Maffione 		DPRINTF("Stream not found");
6016b021cc2SWarner Losh 		return (0);
6026b021cc2SWarner Losh 	}
6036b021cc2SWarner Losh 
6046b021cc2SWarner Losh 	actx = &st->actx;
6056b021cc2SWarner Losh 
6066b021cc2SWarner Losh 	if (run)
6076b021cc2SWarner Losh 		err = hda_audio_ctxt_start(actx);
6086b021cc2SWarner Losh 	else
6096b021cc2SWarner Losh 		err = hda_audio_ctxt_stop(actx);
6106b021cc2SWarner Losh 
6116b021cc2SWarner Losh 	return (err);
6126b021cc2SWarner Losh }
6136b021cc2SWarner Losh 
6146b021cc2SWarner Losh static int
6156b021cc2SWarner Losh hda_codec_parse_format(uint16_t fmt, struct audio_params *params)
6166b021cc2SWarner Losh {
6176b021cc2SWarner Losh 	uint8_t div = 0;
6186b021cc2SWarner Losh 
6196b021cc2SWarner Losh 	assert(params);
6206b021cc2SWarner Losh 
6216b021cc2SWarner Losh 	/* Compute the Sample Rate */
6226b021cc2SWarner Losh 	params->rate = (fmt & HDA_CODEC_FMT_BASE_MASK) ? 44100 : 48000;
6236b021cc2SWarner Losh 
6246b021cc2SWarner Losh 	switch (fmt & HDA_CODEC_FMT_MULT_MASK) {
6256b021cc2SWarner Losh 	case HDA_CODEC_FMT_MULT_2:
6266b021cc2SWarner Losh 		params->rate *= 2;
6276b021cc2SWarner Losh 		break;
6286b021cc2SWarner Losh 	case HDA_CODEC_FMT_MULT_3:
6296b021cc2SWarner Losh 		params->rate *= 3;
6306b021cc2SWarner Losh 		break;
6316b021cc2SWarner Losh 	case HDA_CODEC_FMT_MULT_4:
6326b021cc2SWarner Losh 		params->rate *= 4;
6336b021cc2SWarner Losh 		break;
6346b021cc2SWarner Losh 	}
6356b021cc2SWarner Losh 
6366b021cc2SWarner Losh 	div = (fmt >> HDA_CODEC_FMT_DIV_SHIFT) & HDA_CODEC_FMT_DIV_MASK;
6376b021cc2SWarner Losh 	params->rate /= (div + 1);
6386b021cc2SWarner Losh 
6396b021cc2SWarner Losh 	/* Compute the Bits per Sample */
6406b021cc2SWarner Losh 	switch (fmt & HDA_CODEC_FMT_BITS_MASK) {
6416b021cc2SWarner Losh 	case HDA_CODEC_FMT_BITS_8:
6426b021cc2SWarner Losh 		params->format = AFMT_U8;
6436b021cc2SWarner Losh 		break;
6446b021cc2SWarner Losh 	case HDA_CODEC_FMT_BITS_16:
6456b021cc2SWarner Losh 		params->format = AFMT_S16_LE;
6466b021cc2SWarner Losh 		break;
6476b021cc2SWarner Losh 	case HDA_CODEC_FMT_BITS_24:
6486b021cc2SWarner Losh 		params->format = AFMT_S24_LE;
6496b021cc2SWarner Losh 		break;
6506b021cc2SWarner Losh 	case HDA_CODEC_FMT_BITS_32:
6516b021cc2SWarner Losh 		params->format = AFMT_S32_LE;
6526b021cc2SWarner Losh 		break;
6536b021cc2SWarner Losh 	default:
654332eff95SVincenzo Maffione 		DPRINTF("Unknown format bits: 0x%x",
6556b021cc2SWarner Losh 		    fmt & HDA_CODEC_FMT_BITS_MASK);
6566b021cc2SWarner Losh 		return (-1);
6576b021cc2SWarner Losh 	}
6586b021cc2SWarner Losh 
6596b021cc2SWarner Losh 	/* Compute the Number of Channels */
6606b021cc2SWarner Losh 	params->channels = (fmt & HDA_CODEC_FMT_CHAN_MASK) + 1;
6616b021cc2SWarner Losh 
6626b021cc2SWarner Losh 	return (0);
6636b021cc2SWarner Losh }
6646b021cc2SWarner Losh 
6656b021cc2SWarner Losh static uint32_t
6666b021cc2SWarner Losh hda_codec_audio_output_nid(struct hda_codec_softc *sc, uint16_t verb,
6676b021cc2SWarner Losh     uint16_t payload)
6686b021cc2SWarner Losh {
6696b021cc2SWarner Losh 	struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
6706b021cc2SWarner Losh 	int res;
6716b021cc2SWarner Losh 
6726b021cc2SWarner Losh 	res = hda_codec_audio_inout_nid(st, verb, payload);
6736b021cc2SWarner Losh 
6746b021cc2SWarner Losh 	return (res);
6756b021cc2SWarner Losh }
6766b021cc2SWarner Losh 
6776b021cc2SWarner Losh static void
6786b021cc2SWarner Losh hda_codec_audio_output_do_transfer(void *arg)
6796b021cc2SWarner Losh {
6806b021cc2SWarner Losh 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
6816b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
6826b021cc2SWarner Losh 	struct hda_ops *hops = NULL;
6836b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
6846b021cc2SWarner Losh 	struct audio *aud = NULL;
6856b021cc2SWarner Losh 	int err;
6866b021cc2SWarner Losh 
6876b021cc2SWarner Losh 	hci = sc->hci;
6886b021cc2SWarner Losh 	assert(hci);
6896b021cc2SWarner Losh 
6906b021cc2SWarner Losh 	hops = hci->hops;
6916b021cc2SWarner Losh 	assert(hops);
6926b021cc2SWarner Losh 
6936b021cc2SWarner Losh 	st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
6946b021cc2SWarner Losh 	aud = st->aud;
6956b021cc2SWarner Losh 
6966b021cc2SWarner Losh 	err = hops->transfer(hci, st->stream, 1, st->buf, sizeof(st->buf));
6976b021cc2SWarner Losh 	if (err)
6986b021cc2SWarner Losh 		return;
6996b021cc2SWarner Losh 
7006b021cc2SWarner Losh 	err = audio_playback(aud, st->buf, sizeof(st->buf));
7016b021cc2SWarner Losh 	assert(!err);
7026b021cc2SWarner Losh }
7036b021cc2SWarner Losh 
7046b021cc2SWarner Losh static int
7056b021cc2SWarner Losh hda_codec_audio_output_do_setup(void *arg)
7066b021cc2SWarner Losh {
7076b021cc2SWarner Losh 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
7086b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
7096b021cc2SWarner Losh 	struct audio *aud = NULL;
7106b021cc2SWarner Losh 	struct audio_params params;
7116b021cc2SWarner Losh 	int err;
7126b021cc2SWarner Losh 
7136b021cc2SWarner Losh 	st = &sc->streams[HDA_CODEC_STREAM_OUTPUT];
7146b021cc2SWarner Losh 	aud = st->aud;
7156b021cc2SWarner Losh 
7166b021cc2SWarner Losh 	err = hda_codec_parse_format(st->fmt, &params);
7176b021cc2SWarner Losh 	if (err)
7186b021cc2SWarner Losh 		return (-1);
7196b021cc2SWarner Losh 
720332eff95SVincenzo Maffione 	DPRINTF("rate: %d, channels: %d, format: 0x%x",
7216b021cc2SWarner Losh 	    params.rate, params.channels, params.format);
7226b021cc2SWarner Losh 
7236b021cc2SWarner Losh 	return (audio_set_params(aud, &params));
7246b021cc2SWarner Losh }
7256b021cc2SWarner Losh 
7266b021cc2SWarner Losh static uint32_t
7276b021cc2SWarner Losh hda_codec_audio_input_nid(struct hda_codec_softc *sc, uint16_t verb,
7286b021cc2SWarner Losh     uint16_t payload)
7296b021cc2SWarner Losh {
7306b021cc2SWarner Losh 	struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_INPUT];
7316b021cc2SWarner Losh 	int res;
7326b021cc2SWarner Losh 
7336b021cc2SWarner Losh 	res = hda_codec_audio_inout_nid(st, verb, payload);
7346b021cc2SWarner Losh 
7356b021cc2SWarner Losh 	return (res);
7366b021cc2SWarner Losh }
7376b021cc2SWarner Losh 
7386b021cc2SWarner Losh static void
7396b021cc2SWarner Losh hda_codec_audio_input_do_transfer(void *arg)
7406b021cc2SWarner Losh {
7416b021cc2SWarner Losh 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
7426b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
7436b021cc2SWarner Losh 	struct hda_ops *hops = NULL;
7446b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
7456b021cc2SWarner Losh 	struct audio *aud = NULL;
7466b021cc2SWarner Losh 	int err;
7476b021cc2SWarner Losh 
7486b021cc2SWarner Losh 	hci = sc->hci;
7496b021cc2SWarner Losh 	assert(hci);
7506b021cc2SWarner Losh 
7516b021cc2SWarner Losh 	hops = hci->hops;
7526b021cc2SWarner Losh 	assert(hops);
7536b021cc2SWarner Losh 
7546b021cc2SWarner Losh 	st = &sc->streams[HDA_CODEC_STREAM_INPUT];
7556b021cc2SWarner Losh 	aud = st->aud;
7566b021cc2SWarner Losh 
7576b021cc2SWarner Losh 	err = audio_record(aud, st->buf, sizeof(st->buf));
7586b021cc2SWarner Losh 	assert(!err);
7596b021cc2SWarner Losh 
7606b021cc2SWarner Losh 	hops->transfer(hci, st->stream, 0, st->buf, sizeof(st->buf));
7616b021cc2SWarner Losh }
7626b021cc2SWarner Losh 
7636b021cc2SWarner Losh static int
7646b021cc2SWarner Losh hda_codec_audio_input_do_setup(void *arg)
7656b021cc2SWarner Losh {
7666b021cc2SWarner Losh 	struct hda_codec_softc *sc = (struct hda_codec_softc *)arg;
7676b021cc2SWarner Losh 	struct hda_codec_stream *st = NULL;
7686b021cc2SWarner Losh 	struct audio *aud = NULL;
7696b021cc2SWarner Losh 	struct audio_params params;
7706b021cc2SWarner Losh 	int err;
7716b021cc2SWarner Losh 
7726b021cc2SWarner Losh 	st = &sc->streams[HDA_CODEC_STREAM_INPUT];
7736b021cc2SWarner Losh 	aud = st->aud;
7746b021cc2SWarner Losh 
7756b021cc2SWarner Losh 	err = hda_codec_parse_format(st->fmt, &params);
7766b021cc2SWarner Losh 	if (err)
7776b021cc2SWarner Losh 		return (-1);
7786b021cc2SWarner Losh 
779332eff95SVincenzo Maffione 	DPRINTF("rate: %d, channels: %d, format: 0x%x",
7806b021cc2SWarner Losh 	    params.rate, params.channels, params.format);
7816b021cc2SWarner Losh 
7826b021cc2SWarner Losh 	return (audio_set_params(aud, &params));
7836b021cc2SWarner Losh }
7846b021cc2SWarner Losh 
7856b021cc2SWarner Losh static uint32_t
7866b021cc2SWarner Losh hda_codec_audio_inout_nid(struct hda_codec_stream *st, uint16_t verb,
7876b021cc2SWarner Losh     uint16_t payload)
7886b021cc2SWarner Losh {
7896b021cc2SWarner Losh 	uint32_t res = 0;
7906b021cc2SWarner Losh 	uint8_t mute = 0;
7916b021cc2SWarner Losh 	uint8_t gain = 0;
7926b021cc2SWarner Losh 
793332eff95SVincenzo Maffione 	DPRINTF("%s verb: 0x%x, payload, 0x%x", st->actx.name, verb, payload);
7946b021cc2SWarner Losh 
7956b021cc2SWarner Losh 	switch (verb) {
7966b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_CONV_FMT:
7976b021cc2SWarner Losh 		res = st->fmt;
7986b021cc2SWarner Losh 		break;
7996b021cc2SWarner Losh 	case HDA_CMD_VERB_SET_CONV_FMT:
8006b021cc2SWarner Losh 		st->fmt = payload;
8016b021cc2SWarner Losh 		break;
8026b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_AMP_GAIN_MUTE:
8036b021cc2SWarner Losh 		if (payload & HDA_CMD_GET_AMP_GAIN_MUTE_LEFT) {
8046b021cc2SWarner Losh 			res = st->left_gain | st->left_mute;
805332eff95SVincenzo Maffione 			DPRINTF("GET_AMP_GAIN_MUTE_LEFT: 0x%x", res);
8066b021cc2SWarner Losh 		} else {
8076b021cc2SWarner Losh 			res = st->right_gain | st->right_mute;
808332eff95SVincenzo Maffione 			DPRINTF("GET_AMP_GAIN_MUTE_RIGHT: 0x%x", res);
8096b021cc2SWarner Losh 		}
8106b021cc2SWarner Losh 		break;
8116b021cc2SWarner Losh 	case HDA_CMD_VERB_SET_AMP_GAIN_MUTE:
8126b021cc2SWarner Losh 		mute = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE;
8136b021cc2SWarner Losh 		gain = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK;
8146b021cc2SWarner Losh 
8156b021cc2SWarner Losh 		if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_LEFT) {
8166b021cc2SWarner Losh 			st->left_mute = mute;
8176b021cc2SWarner Losh 			st->left_gain = gain;
8186b021cc2SWarner Losh 			DPRINTF("SET_AMP_GAIN_MUTE_LEFT: \
819332eff95SVincenzo Maffione 			    mute: 0x%x gain: 0x%x", mute, gain);
8206b021cc2SWarner Losh 		}
8216b021cc2SWarner Losh 
8226b021cc2SWarner Losh 		if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT) {
8236b021cc2SWarner Losh 			st->right_mute = mute;
8246b021cc2SWarner Losh 			st->right_gain = gain;
8256b021cc2SWarner Losh 			DPRINTF("SET_AMP_GAIN_MUTE_RIGHT: \
826332eff95SVincenzo Maffione 			    mute: 0x%x gain: 0x%x", mute, gain);
8276b021cc2SWarner Losh 		}
8286b021cc2SWarner Losh 		break;
8296b021cc2SWarner Losh 	case HDA_CMD_VERB_GET_CONV_STREAM_CHAN:
8306b021cc2SWarner Losh 		res = (st->stream << 4) | st->channel;
8316b021cc2SWarner Losh 		break;
8326b021cc2SWarner Losh 	case HDA_CMD_VERB_SET_CONV_STREAM_CHAN:
8336b021cc2SWarner Losh 		st->channel = payload & 0x0f;
8346b021cc2SWarner Losh 		st->stream = (payload >> 4) & 0x0f;
835332eff95SVincenzo Maffione 		DPRINTF("st->channel: 0x%x st->stream: 0x%x",
8366b021cc2SWarner Losh 		    st->channel, st->stream);
8376b021cc2SWarner Losh 		if (!st->stream)
8386b021cc2SWarner Losh 			hda_audio_ctxt_stop(&st->actx);
8396b021cc2SWarner Losh 		break;
8406b021cc2SWarner Losh 	default:
841332eff95SVincenzo Maffione 		DPRINTF("Unknown VERB: 0x%x", verb);
8426b021cc2SWarner Losh 		break;
8436b021cc2SWarner Losh 	}
8446b021cc2SWarner Losh 
8456b021cc2SWarner Losh 	return (res);
8466b021cc2SWarner Losh }
8476b021cc2SWarner Losh 
848*37045dfaSMark Johnston static const struct hda_codec_class hda_codec = {
8496b021cc2SWarner Losh 	.name		= "hda_codec",
8506b021cc2SWarner Losh 	.init		= hda_codec_init,
8516b021cc2SWarner Losh 	.reset		= hda_codec_reset,
8526b021cc2SWarner Losh 	.command	= hda_codec_command,
8536b021cc2SWarner Losh 	.notify		= hda_codec_notify,
8546b021cc2SWarner Losh };
8556b021cc2SWarner Losh HDA_EMUL_SET(hda_codec);
8566b021cc2SWarner Losh 
8576b021cc2SWarner Losh /*
8586b021cc2SWarner Losh  * HDA Audio Context module function definitions
8596b021cc2SWarner Losh  */
8606b021cc2SWarner Losh 
8616b021cc2SWarner Losh static void *
8626b021cc2SWarner Losh hda_audio_ctxt_thr(void *arg)
8636b021cc2SWarner Losh {
8646b021cc2SWarner Losh 	struct hda_audio_ctxt *actx = arg;
8656b021cc2SWarner Losh 
866332eff95SVincenzo Maffione 	DPRINTF("Start Thread: %s", actx->name);
8676b021cc2SWarner Losh 
8686b021cc2SWarner Losh 	pthread_mutex_lock(&actx->mtx);
8696b021cc2SWarner Losh 	while (1) {
8706b021cc2SWarner Losh 		while (!actx->run)
8716b021cc2SWarner Losh 			pthread_cond_wait(&actx->cond, &actx->mtx);
8726b021cc2SWarner Losh 
8736b021cc2SWarner Losh 		actx->do_transfer(actx->priv);
8746b021cc2SWarner Losh 	}
8756b021cc2SWarner Losh 	pthread_mutex_unlock(&actx->mtx);
8766b021cc2SWarner Losh 
8776b021cc2SWarner Losh 	pthread_exit(NULL);
8786b021cc2SWarner Losh 	return (NULL);
8796b021cc2SWarner Losh }
8806b021cc2SWarner Losh 
8816b021cc2SWarner Losh static int
8826b021cc2SWarner Losh hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname,
8836b021cc2SWarner Losh     transfer_func_t do_transfer, setup_func_t do_setup, void *priv)
8846b021cc2SWarner Losh {
8856b021cc2SWarner Losh 	int err;
8866b021cc2SWarner Losh 
8876b021cc2SWarner Losh 	assert(actx);
8886b021cc2SWarner Losh 	assert(tname);
8896b021cc2SWarner Losh 	assert(do_transfer);
8906b021cc2SWarner Losh 	assert(do_setup);
8916b021cc2SWarner Losh 	assert(priv);
8926b021cc2SWarner Losh 
8936b021cc2SWarner Losh 	memset(actx, 0, sizeof(*actx));
8946b021cc2SWarner Losh 
8956b021cc2SWarner Losh 	actx->run = 0;
8966b021cc2SWarner Losh 	actx->do_transfer = do_transfer;
8976b021cc2SWarner Losh 	actx->do_setup = do_setup;
8986b021cc2SWarner Losh 	actx->priv = priv;
8996b021cc2SWarner Losh 	if (strlen(tname) < sizeof(actx->name))
9006b021cc2SWarner Losh 		memcpy(actx->name, tname, strlen(tname) + 1);
9016b021cc2SWarner Losh 	else
9026b021cc2SWarner Losh 		strcpy(actx->name, "unknown");
9036b021cc2SWarner Losh 
9046b021cc2SWarner Losh 	err = pthread_mutex_init(&actx->mtx, NULL);
9056b021cc2SWarner Losh 	assert(!err);
9066b021cc2SWarner Losh 
9076b021cc2SWarner Losh 	err = pthread_cond_init(&actx->cond, NULL);
9086b021cc2SWarner Losh 	assert(!err);
9096b021cc2SWarner Losh 
9106b021cc2SWarner Losh 	err = pthread_create(&actx->tid, NULL, hda_audio_ctxt_thr, actx);
9116b021cc2SWarner Losh 	assert(!err);
9126b021cc2SWarner Losh 
9136b021cc2SWarner Losh 	pthread_set_name_np(actx->tid, tname);
9146b021cc2SWarner Losh 
9156b021cc2SWarner Losh 	actx->started = 1;
9166b021cc2SWarner Losh 
9176b021cc2SWarner Losh 	return (0);
9186b021cc2SWarner Losh }
9196b021cc2SWarner Losh 
9206b021cc2SWarner Losh static int
9216b021cc2SWarner Losh hda_audio_ctxt_start(struct hda_audio_ctxt *actx)
9226b021cc2SWarner Losh {
9236b021cc2SWarner Losh 	int err = 0;
9246b021cc2SWarner Losh 
9256b021cc2SWarner Losh 	assert(actx);
9266b021cc2SWarner Losh 	assert(actx->started);
9276b021cc2SWarner Losh 
9286b021cc2SWarner Losh 	/* The stream is supposed to be stopped */
9296b021cc2SWarner Losh 	if (actx->run)
9306b021cc2SWarner Losh 		return (-1);
9316b021cc2SWarner Losh 
9326b021cc2SWarner Losh 	pthread_mutex_lock(&actx->mtx);
9336b021cc2SWarner Losh 	err = (* actx->do_setup)(actx->priv);
9346b021cc2SWarner Losh 	if (!err) {
9356b021cc2SWarner Losh 		actx->run = 1;
9366b021cc2SWarner Losh 		pthread_cond_signal(&actx->cond);
9376b021cc2SWarner Losh 	}
9386b021cc2SWarner Losh 	pthread_mutex_unlock(&actx->mtx);
9396b021cc2SWarner Losh 
9406b021cc2SWarner Losh 	return (err);
9416b021cc2SWarner Losh }
9426b021cc2SWarner Losh 
9436b021cc2SWarner Losh static int
9446b021cc2SWarner Losh hda_audio_ctxt_stop(struct hda_audio_ctxt *actx)
9456b021cc2SWarner Losh {
9466b021cc2SWarner Losh 	actx->run = 0;
9476b021cc2SWarner Losh 	return (0);
9486b021cc2SWarner Losh }
949