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