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