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, ¶ms);
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, ¶ms));
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, ¶ms);
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, ¶ms));
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