From d8c9001418e6172099a0907f022534f152e29d71 Mon Sep 17 00:00:00 2001 From: Alexander Egorenkov Date: Sat, 21 May 2022 12:05:30 +0200 Subject: [PATCH] CS8409 TAS5764L support Signed-off-by: Alexander Egorenkov --- Makefile | 5 +- patch_cirrus_apple.h | 3083 ++++++++++++++++ patch_cirrus_boot84.h | 2452 +++++++++++++ patch_cirrus_hda_generic_copy.h | 432 +++ patch_cirrus_new84.h | 1906 ++++++++++ patch_cirrus_real84.h | 5954 +++++++++++++++++++++++++++++++ patch_cirrus_real84_i2c.h | 1955 ++++++++++ patch_cs8409.c | 19 + patch_cs8409.h | 140 + 9 files changed, 15943 insertions(+), 3 deletions(-) create mode 100644 patch_cirrus_apple.h create mode 100644 patch_cirrus_boot84.h create mode 100644 patch_cirrus_hda_generic_copy.h create mode 100644 patch_cirrus_new84.h create mode 100644 patch_cirrus_real84.h create mode 100644 patch_cirrus_real84_i2c.h diff --git a/Makefile b/Makefile index 082218d..9232ee4 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,9 @@ snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o # debug build flags -#KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_DEBUG=1 -DMYSOUNDDEBUGFULL -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" +#KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_DEBUG=1 -DMYSOUNDDEBUGFULL -DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" # normal build flags -KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" - +KBUILD_EXTRA_CFLAGS = "-DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function" ifdef KVER KDIR := /lib/modules/$(KVER) diff --git a/patch_cirrus_apple.h b/patch_cirrus_apple.h new file mode 100644 index 0000000..9a9311e --- /dev/null +++ b/patch_cirrus_apple.h @@ -0,0 +1,3083 @@ +//#include +#include +#include + +#include + +//#include "hda_generic.h" + +//#include "patch_cs8409.h" + + + +// define some explicit debugging print functions +// under flag control so can be easily turned off + + +#ifdef MYSOUNDDEBUGFULL +#define mycodec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mycodec_i2c_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mydev_info(codecdev, fmt, args...) \ + dev_info(codecdev, fmt, ##args) +#define mycodec_dbg(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define myprintk_dbg(fmt, args...) \ + printk(fmt, ##args) +#define myprintk(fmt, args...) \ + printk(fmt, ##args) +#else +#define mycodec_dbg(...) +#define myprintk_dbg(...) +#ifdef MYSOUNDDEBUG +#define mycodec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mycodec_i2c_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define mydev_info(codecdev, fmt, args...) \ + dev_info(codecdev, fmt, ##args) +#define myprintk(fmt, args...) \ + printk(fmt, ##args) +#else +#define mycodec_info(...) +#define mycodec_i2c_info(...) +#define mydev_info(...) +#define myprintk(...) +#endif +#endif + + +/* + + info for enum for checking initial assignment + +enum { + AC_JACK_LINE_OUT, 0x0 + AC_JACK_SPEAKER, 0x1 + AC_JACK_HP_OUT, 0x2 + AC_JACK_CD, 0x3 + AC_JACK_SPDIF_OUT, 0x4 + AC_JACK_DIG_OTHER_OUT, 0x5 + AC_JACK_MODEM_LINE_SIDE, 0x6 + AC_JACK_MODEM_HAND_SIDE, 0x7 + AC_JACK_LINE_IN, 0x8 + AC_JACK_AUX, 0x9 + AC_JACK_MIC_IN, 0xa + AC_JACK_TELEPHONY, 0xb + AC_JACK_SPDIF_IN, 0xc + AC_JACK_DIG_OTHER_IN, 0xd + AC_JACK_OTHER = 0xf, 0xf +}; + */ + + +/* + + it appears from the cs42l42 definitions in patch_cs8409.h that + each nid is associated with a specific Audio Serial Port + + nids as used in Apple + output + speaker 0x002 -> 0x024 CS8409_PIN_ASP1_OUT_A -> CS8409_PIN_ASP1_TRANSMITTER_A + speaker 0x003 -> 0x025 CS8409_PIN_ASP1_OUT_B -> CS8409_PIN_ASP1_TRANSMITTER_B + headphones 0x00a -> 0x02c CS8409_PIN_ASP2_OUT_A -> CS8409_PIN_ASP2_TRANSMITTER_A + input + headset mike 0x03c -> 0x01a CS8409_PIN_ASP2_RECEIVER_A -> CS8409_PIN_ASP2_IN_A + macbook pros + internal mike 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1 + linein 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2 + imacs + linein 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1 + internal mike 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2 + + */ + + +/* CS8409 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +// for the moment leave my old definition names but map to the new module enum values +#define CS8409_IDX_DEV_CFG CS8409_PIN_AFG +#define CS8409_VENDOR_NID CS8409_PIN_VENDOR_WIDGET +#define CS8409_BEEP_NID CS8409_PIN_BEEP_GEN +#else +#define CS8409_IDX_DEV_CFG 0x01 +#define CS8409_VENDOR_NID 0x47 +#define CS8409_BEEP_NID 0x46 +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +// nid devs based on Dell fixups +#define CS8409_CS42L83_HP_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A +#define CS8409_CS42L83_HP_MIC_PIN_NID CS8409_PIN_ASP2_RECEIVER_A +#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID CS8409_PIN_DMIC1_IN +#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID CS8409_PIN_DMIC2_IN +#define CS8409_CS42L83_IMAC_MIC_PIN_NID CS8409_PIN_DMIC2_IN +#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID CS8409_PIN_DMIC1_IN +#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC2 +#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC1 + +// add in coding based on Dell fixups + +#define CS42L83_I2C_ADDR 0x90 // for some reason given as (0x48 << 1) +#define CS8409_CS42L83_RESET 0x02 // gpio interrupt mask - see cs42l83_external_control_GPIO +#define CS8409_CS42L83_INT 0x01 // gpio interrupt mask + +#else + +#define CS8409_CS42L83_HP_PIN_NID 0x2c +#define CS8409_CS42L83_HP_MIC_PIN_NID 0x3c +#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID 0x44 +#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID 0x45 +#define CS8409_CS42L83_IMAC_MIC_PIN_NID 0x45 +#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID 0x44 +#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID 0x23 +#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID 0x22 + +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +// this is a list of i2c commands for init +static const struct cs8409_i2c_param cs42l83_init_reg_seq[] = { + //{ 0x0000, 0x00 }, +}; + +struct sub_codec cs8409_cs42l83_codec = { + .addr = CS42L83_I2C_ADDR, + .reset_gpio = CS8409_CS42L83_RESET, + .irq_mask = CS8409_CS42L83_INT, + .init_seq = cs42l83_init_reg_seq, + .init_seq_num = ARRAY_SIZE(cs42l83_init_reg_seq), + .hp_jack_in = 0, + .mic_jack_in = 0, + .linein_jack_in = 0, + .paged = 1, + .suspended = 1, + .no_type_dect = 0, +}; + + +// define the fixed stream format + +const struct hda_pcm_stream cs42l83_apple_pcm_analog_playback = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ +}; + +const struct hda_pcm_stream cs42l83_apple_pcm_analog_capture = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ +}; + + +#endif + +/* + + register mask values collated from Microsoft cs4208_39.inf file plus the new Dell cs8409 module + + CS8409_DEV_CFG1, // reg 0x00 + 0xb008 +PLL1/2_EN, +I2C_EN - .inf file + 0xb000 +PLL1/2_EN - .inf file + 0x9008 +PLL1_EN, +I2C_EN - .inf file + 0x9000 +PLL1_EN - .inf file + CS8409_DEV_CFG2, // reg 0x01 + 0x0006 ASP1/2_EN = 0, ASP1/2_STP = 1 + 0x0066 ASP1/2_EN = 1, ASP1/2_STP = 1 + 0x0022 ASP1_EN = 1, ASP1_STP = 1 ? + 0x0044 ASP2_EN = 1, ASP2_STP = 1 ? + 0x0200 seen in Apple but unknown + CS8409_DEV_CFG3, // reg 0x02 + 0x0a80 ASP1/2_BUS_IDLE=10, +GPIO_I2C + 0x0280 seen in Apple ASP1_BUS_IDLE=10?, +GPIO_I2C + CS8409_ASP1_CLK_CTRL1, // reg 0x03 + 0x8000 ASP1: LCHI = 00h + CS8409_ASP1_CLK_CTRL2, // reg 0x04 + 0x28ff ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh + CS8409_ASP1_CLK_CTRL3, // reg 0x05 + 0x0062 ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 + 0b0110 0b0010 + so FSD = 0x0060 + 0x005a ASP1: MCEN = 0, FSD = 010, SCPOL_IN/OUT = 1, SCDIV = 1:4 + 0b0101 0b1010 + so FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV = 0x0002 + 0x0001 seen in Apple but what bit is this?? MCEN?? + CS8409_ASP2_CLK_CTRL1, // reg 0x06 + 0x8000 ASP1: LCHI = 00h + CS8409_ASP2_CLK_CTRL2, // reg 0x07 + 0x283f ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh + CS8409_ASP2_CLK_CTRL3, // reg 0x08 + 0x805c ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 + 0b0101 0b1100 + 5050 = 0x8000?, FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV=1:16 = 0x0004? + CS8409_DMIC_CFG, // reg 0x09 + 0x0023 DMIC1_MO=10b, DMIC1/2_SR=1 + 0x0033 seen in Apple + 0x00b3 seen in Apple + 0x01b3 seen in Apple + CS8409_BEEP_CFG, // reg 0x0a + + ASP2_Rx_NULL_INS_RMV, // reg 0x11 + 0x0001 seen in Apple + ASP2_Rx_RATE1, // reg 0x12 + 0xaccc seen in Apple + ASP2_Tx_NULL_INS_RMV, // reg 0x14 + 0x0100 seen in Apple + ASP2_Tx_RATE1, // reg 0x15 + 0xaaaa seen in Apple + ASP1_SYNC_CTRL, // reg 0x17 + 0x0000 seen in Apple sync converters + 0x0001 seen in Apple sync converters + 0x0002 seen in Apple sync converters + 0x0003 seen in Apple sync converters + ASP2_SYNC_CTRL, // reg 0x18 + 0x0000 seen in Apple sync converters + + the following registers are defined per output nid + from A to H for nid 0x02 to 0x09 ASP1 + from A to H for nid 0x0a to 0x11 ASP2 + the following registers are defined per input nid + from A to H for nid 0x12 to 0x19 ASP1 + from A to H for nid 0x1a to 0x21 ASP2 + + ASP1_A_TX_CTRL1 // reg 0x19 + nide 0x02 + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP1_A_TX_CTRL2 // reg 0x1a + nide 0x02 + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + ASP1_B_TX_CTRL1, // reg 0x1b + nide 0x03 + 0x0840 seen in Apple - on + 0x8840 seen in Apple - off + ASP1_B_TX_CTRL2, // reg 0x1c + nide 0x03 + 0x0860 seen in Apple - on + 0x8860 seen in Apple - off + + ASP2_A_TX_CTRL1, // reg 0x29 + nide 0x0a + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP2_A_TX_CTRL2, // reg 0x2a + nide 0x0a + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + + ASP2_A_RX_CTRL1, // reg 0x49 + nide 0x1a + 0x0800 seen in Apple - on + 0x8800 seen in Apple - off + ASP2_A_RX_CTRL2, // reg 0x4a + nide 0x1a + 0x0820 seen in Apple - on + 0x8820 seen in Apple - off + + CS8409_ASP1_INTRN_STS, // reg 0x6b + 0x001f seen in Apple + CS8409_ASP2_INTRN_STS, // reg 0x6c + 0x001f seen in Apple + + CS8409_ASP_UNS_RESP_MASK, // reg 0x71 + 0x400f seen in Apple + 0x800f seen in Apple + 0xc00f seen in Apple + + CS8409_PAD_CFG_SLW_RATE_CTRL // reg 0x82 + it appears to contain 2 sort of separate items - the ASP1 and ASP2 enables and the DMIC1/DMIC2 SCL enables + 0xfc03 ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1/2_SCL_EN=1 (was DMIC1_SCL_EN in comments but thinks thats wrong given below) + 0xfc01 (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1) + 0xff03 (ASP1/2_xxx_EN = 1, DMIC1/2_SCL_EN = 1) + 0xfd02 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 1, ASP2_MCLK_EN = 0, DMIC2_SCL_EN = 1) + 0xfe03 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 0, ASP2_MCLK_EN = 1, DMIC1/2_SCL_EN = 1) + so (ASP1_MCLK_EN is 0x0100 and ASP2_MCLK_EN is 0x0200) + from the OSX codes we seem to have + 0xa800 ASP2_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + 0x5400 ASP1_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + + + */ + +/* + + Interrupt analysis (mainly from cs42l42 manual) + + actual button presses are 0x01, 0x02 and button release 0x10 + for 0x1b7c 0x02 is a short release for buttons, 0x08 is reserved + the mask bits for 0x1b7a seem to be 0xe7 for button detect defining 0x18 as the button detect interrupt(s) + and 0xdc for actual button interrupts + (0x1b79 is mask, 0x1b7b status; 0x1b7a is mask, 0x1b7c is presumed status, 0x131b is mask, 0x1308 status, + 0x1320 is mask, 0x130f status) + + 0x1b79 Detect Interrupt Mask 1 + 0x80 M_HSBIAS_SENSE + 0x40 M_TIP_SENSE_PLUG + 0x20 M_TIP_SENSE_UNPLUG + 0x1b7b Detect Interrupt Status 1 - assumed - not documented in cs42l42 but listed in fig 4-45 + 0x80 M_HSBIAS_SENSE - assumed + 0x40 M_TIP_SENSE_PLUG - assumed + 0x20 M_TIP_SENSE_UNPLUG - assumed + 0x1b7a Detect Interrupt Mask 2 (Buttons) + cs42l42 values documented: + 0x80 M_DETECT_TRUE_FALSE + 0x40 M_DETECT_FALSE_TRUE + 0x04 M_HSBIAS_HIZ + 0x02 M_SHORT_RELEASE + 0x01 M_SHORT_DETECTED + OSX button masks seen are 0xff, 0xe7 and 0xdc + so for 0xe7 mask status bits are 0x18 + so for 0xdc mask status bits are 0x23 + bits 0x38 are not documented for cs42l42 + 0xe7 is used for button detection + 0xdc is used for button responses + 0x1b7c Detect Interrupt Status 2 (Buttons) + - cs42l42 documents a 0x130a as Detect Interrupt Status 2 + no such register ever used on OSX + assuming this is a difference for the cs42l83 + 0x80 M_DETECT_TRUE_FALSE + 0x40 M_DETECT_FALSE_TRUE + 0x04 M_HSBIAS_HIZ + 0x02 M_SHORT_RELEASE + 0x01 M_SHORT_DETECTED + OSX status values seen + 0x0a after 0xe7 mask set + 0x40 pre configure button response mask + 0x04 pre disable button interrupts + 0x1b78 Detect Status 2 - seems to be associated with buttons and/or mike + 0x02 HS_TRUE - cs42l42 + 0x01 SHORT_TRUE - cs42l42 + OSX values seen + 0x40 button detect + 0x02 mike sense + new values seen for imacs with non-Apple headsets + 0x20 button detect?? + 0x131b Codec Interrupt Mask + 0x02 M_HSDET_AUTO_DONE + 0x01 M_PDN_DONE + 0x1308 Codec Interrupt Status + 0x02 M_HSDET_AUTO_DONE + 0x01 M_PDN_DONE + 0x1320 Tip/Ring Sense Plug/Unplug Interrupt Mask + 0x08 M_TS_UNPLUG + 0x04 M_TS_PLUG + 0x02 M_RS_UNPLUG + 0x01 M_RS_PLUG + 0x130f Tip/Ring Sense Plug/Unplug Interrupt Status + 0x08 M_TS_UNPLUG + 0x04 M_TS_PLUG + 0x02 M_RS_UNPLUG + 0x01 M_RS_PLUG + + register 0x1b7b: + + TIP_SENSE_PLUG 0x40 + TIP_SENSE_UNPLUG 0x20 + + BUTTON_DOWN_PRESS 0x1 + BUTTON_UP_PRESS 0x2 + BUTTON_RELEASE 0x10 + + BUTTONS (BUTTON_UP_PRESS | BUTTON_DOWN_PRESS) + + register 0x1b7c: + + BUTTON_DETECT_MAIN 0x18 // we only see 0x08 but the mask allows for these 2 bits + + register 0x1b78: + + BUTTON_DETECT_MASK 0x60 // now seen 2 bits used - plausibly depends if Apple with buttons (0x40) or non-Apple with buttons (0x20) + BUTTON_DETECT1 0x40 + BUTTON_DETECT2 0x20 + MIKE_CONNECT 0x02 + + register 0x131b: + + HSDET_AUTO_DONE 0x02 + PDN_DONE 0x01 + + + */ + + + +/* + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + +struct unsol_item { + struct list_head list; + unsigned int idx; + unsigned int res; +}; + +struct hda_cvt_setup_apple { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; + +struct cs8409_apple_spec { + struct hda_gen_spec gen; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ + unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* digital beep */ + hda_nid_t beep_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + + // so it appears we have "concurrency" in the linux HDA code + // in that if unsolicited responses occur which perform extensive verbs + // the hda verbs are intermixed with eg extensive start playback verbs + // on OSX we appear to have blocks of verbs during which unsolicited responses + // are logged but the unsolicited verbs occur after the verb block + // this flag is used to flag such verb blocks and the list will store the + // responses + // we use a pre-allocated list - if we have more than 10 outstanding unsols + // we will drop + // not clear if mutexes would be the way to go + int block_unsol; + struct list_head unsol_list; + struct unsol_item unsol_items_prealloc[10]; + int unsol_items_prealloc_used[10]; + + // add in specific nids for the intmike and linein as they seem to swap + // between macbook pros (14,3) and imacs (18,3) + int intmike_nid; + int linein_nid; + int intmike_adc_nid; + int linein_amp_nid; + + // the following flag bits also need swapping + int reg9_intmike_dmic_mo; + int reg9_linein_dmic_mo; + int reg82_intmike_dmic_scl; + int reg82_linein_dmic_scl; + + + // add explicit stream format store entries as per hda_codec using a local definition + // of hda_cvt_setup (which is local to hda_codec.c) + // also use explicit nid versions + // (except that means either need explicit functions for each nid or have to lookup + // nid each time want to use in a generic function with nid argument) + struct hda_cvt_setup_apple nid_0x02; + struct hda_cvt_setup_apple nid_0x03; + struct hda_cvt_setup_apple nid_0x0a; + struct hda_cvt_setup_apple nid_0x22; + struct hda_cvt_setup_apple nid_0x23; + struct hda_cvt_setup_apple nid_0x1a; + + + // new item to deal with jack presence as Apple seems to have barfed + // the HDA spec by using a separate headphone chip + int jack_present; + + // save the type of headphone connected + int headset_type; + + // if headphone has mike or not + int have_mike; + + // if headphone has buttons or not + int have_buttons; + + // current stream channel count + int stream_channels; + + // set when playing for plug/unplug events while playing + int playing; + + // set when capturing for plug/unplug events while capturing + int capturing; + + // changing coding - OSX sets up the format on plugin + // then does some minimal setup when start play + // initial coding delayed any format setup till actually play + // this works for no mike but not for mike - we need to initialize + // the mike on plugin + // this flag will be set when we have done the format setup + // so know if need to do it on play or not + // now need 2 flags - one for play and one for capture + int headset_play_format_setup_needed; + int headset_capture_format_setup_needed; + + int headset_presetup_done; + + + int use_data; + + + // this is new item for dealing with headset plugins + // so can distinguish which phase we are in if have multiple interrupts + // now primarily used to indicate if booted with headset plugged in + int headset_phase; + + // another dirty hack item to manage the different headset enable codes + int headset_enable; + + int play_init; + int capture_init; + + // new item to limit times we redo unmute/play +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 last_play_time; +#else + struct timespec last_play_time; +#endif + // record the first play time - we have a problem there + // some initial plays that I dont understand - so skip any setup + // till sometime after the first play +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 first_play_time; +#else + struct timespec first_play_time; +#endif + +}; +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) +#define cs8409_apple_spec cs8409_spec +#endif + + +// coding copied from hda_generic.c to print the nid path details + + +#define debug_badness(fmt, ...) \ + mycodec_dbg(codec, fmt, ##__VA_ARGS__) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path) +{ + char buf[40]; + char *pos = buf; + int i; + + *pos = 0; + for (i = 0; i < path->depth; i++) + pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", + pos != buf ? ":" : "", + path->path[i]); + + mycodec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); +} + +#else + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path); + +#endif + + +static inline void print_nid_path_idx(struct hda_codec *codec, + const char *pfx, int idx) +{ + struct nid_path *path; + + path = snd_hda_get_path_from_idx(codec, idx); + if (path) + print_nid_path(codec, pfx, path); +} + +static void debug_show_configs(struct hda_codec *codec, + struct auto_pin_cfg *cfg) +{ + struct hda_gen_spec *spec = codec->spec; + static const char * const lo_type[3] = { "LO", "SP", "HP" }; + int i; + + debug_badness("debug_show_configs start\n"); + + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[3], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3], + lo_type[cfg->line_out_type]); + for (i = 0; i < cfg->line_outs; i++) + print_nid_path_idx(codec, " out", spec->out_paths[i]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + for (i = 0; i < spec->multi_ios; i++) + print_nid_path_idx(codec, " mio", + spec->out_paths[cfg->line_outs + i]); + if (cfg->hp_outs) + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[3], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + for (i = 0; i < cfg->hp_outs; i++) + print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); + if (cfg->speaker_outs) + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); + for (i = 0; i < cfg->speaker_outs; i++) + print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); + for (i = 0; i < 3; i++) + print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); + debug_badness("debug_show_configs end\n"); +} + +// attempt at an explicit setup ie not generic +//#include "patch_cirrus_explicit.h" + + +static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action); + +// this is a copy from playback_pcm_prepare in hda_generic.c +// initially I needed to do the Apple setup BEFORE the snd_hda_multi_out_analog_prepare +// in order to overwrite the Apple setup with the actual format/stream id +// NOTA BENE - if playback_pcm_prepare is changed in hda_generic.c then +// those changes must be re-implemented here +// we need this order because snd_hda_multi_out_analog_prepare writes the +// the format and stream id's to the audio nodes +//// so far we have left the Apple setup of the nodes format and stream id's in +// now updated to set the actual format where Apple does the format/stream id setup +// Apples format is very specifically S24_3LE (24 bit), 4 channel, 44.1 kHz +// S24_3LE seems to be very difficult to create so best Ive done is +// S24_LE (24 in 32 bits) or S32_LE +// it seems the digital setup is able to handle this with the Apple TDM +// setup but if we use the normal prepare hook order this overrwites +// the node linux 0x2, 0x3 setup with the Apple setup which leads to noise +// (the HDA specs say the node format setup must match the data) +// if we do the Apple setup and then the snd_hda_multi_out_analog_prepare +// the nodes will have the slightly different but working format +// with proper update of stream format at same point as in Apple log we need to pass +// the actual playback format as passed to this routine to our new "hook" +// cs_8409_pcm_playback_pre_prepare_hook +// to define the cached format correctly in that routine +// so far my analysis is that hinfo stores the stream format in the kernel format style +// but what is passed to cs_8409_playback_pcm_prepare is the format in HDA style +// not yet figured how to convert from kernel format style to HDA style + +static int cs_8409_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + int err; + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare\n"); + + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n", + hinfo->nid, stream_tag, format); + + cs_8409_pcm_playback_pre_prepare_hook(hinfo, codec, stream_tag, format, substream, + HDA_GEN_PCM_ACT_PREPARE); + + // now dont think we need this - we now explicitly copy the 1st 2 channels to 2nd 2 channels + // if given 2 channel input + // plus all headset output is also explicitly being done + // well thats unexpected - if we comment this we loose headphone output + //err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + // stream_tag, format, substream); + err = 0; + + // we cant call directly as call_pcm_playback_hook is local to hda_generic.c + //if (!err) + // call_pcm_playback_hook(hinfo, codec, substream, + // HDA_GEN_PCM_ACT_PREPARE); + // but its a trivial function - at least for the moment!! + if (err) + mycodec_dbg(codec, "cs_8409_playback_pcm_prepare err %d\n", err); + if (!err) + if (spec->pcm_playback_hook) + spec->pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); + return err; +} + + +static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action); + + +// this is a copy from capture_pcm_prepare in hda_generic.c +// NOTA BENE - if capture_pcm_prepare is changed in hda_generic.c then +// those changes must be re-implemented here +static int cs_8409_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_dbg(codec, "cs_8409_capture_pcm_prepare\n"); + + mycodec_dbg(codec, "cs_8409_capture_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n", + hinfo->nid, stream_tag, format); + + + cs_8409_pcm_capture_pre_prepare_hook(hinfo, codec, stream_tag, format, substream, + HDA_GEN_PCM_ACT_PREPARE); + + // we have a problem - this has to handle 2 different types of stream - the internal mike + // and the external headset mike (cs42l83) + + + // NOTE - the following snd_hda_codec_stream no longer do anything + // we have already set the stream data in the pre prepare hook + // - so as the format here is same (or at least should be!!) as that setup there is no format difference to that + // cached and snd_hda_coded_setup_stream does nothing + + if (hinfo->nid == spec->intmike_adc_nid) + { + + // so this is getting stranger and stranger + // the most valid recording is S24_3LE (0x4031) - except that the data we get out is S32_LE (low byte 0) + // - so it doesnt play right - and it messes with arecords vumeter + // (S32_LE is officially 0x4041 - but using that format doesnt seem to have valid data - audio very low) + //// so now try forcing the format here to 0x4031 + //// well that fails miserably - the format mismatch stops data totally + // it now appears we get the same data with either 0x4031 or 0x4041 - both are low volume + // - however scaling (normalizing) in audacity we get the right sound with similar quality to OSX + // so now think the low volume is right - and OSX must be scaling/processing the data in CoreAudio + // (is the internal mike a fake 24 bits - ie its actually 16 bits but stuffed in the low end of the + // 24 bits - hence low volume - preliminary scaling attempts in audacity suggest this might be true!!) + + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + + } + else if (hinfo->nid == 0x1a) + { + + // do we need a pre-prepare function?? + // maybe for this the external mike ie cs42l83 input + + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + + } + else + dev_info(hda_codec_dev(codec), "cs_8409_capture_pcm_prepare - UNIMPLEMENTED input nid 0x%x\n",hinfo->nid); + + // we cant call directly as call_pcm_capture_hook is local to hda_generic.c + //call_pcm_capture_hook(hinfo, codec, substream, + // HDA_GEN_PCM_ACT_PREPARE); + // but its a trivial function - at least for the moment!! + // note this hook if defined also needs to switch between the 2 versions of input!! + if (spec->gen.pcm_capture_hook) + spec->gen.pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); + + return 0; +} + + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_tbl * +cs8409_any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + if (!nid || !jack) + return NULL; + for (i = 0; i < codec->jacktbl.used; i++, jack++) + if (jack->nid == nid) + return jack; + return NULL; +} + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_tbl * +cs_8409_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_mst(codec, nid, dev_id); +#else + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); +#endif + struct hda_jack_tbl *existing_nid_jack = cs8409_any_jack_tbl_get_from_nid(codec, nid); + + WARN_ON(dev_id != 0 && !codec->dp_mst); + + if (jack) + return jack; + jack = snd_array_new(&codec->jacktbl); + if (!jack) + return NULL; + jack->nid = nid; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + jack->dev_id = dev_id; +#endif + jack->jack_dirty = 1; + if (existing_nid_jack) { + jack->tag = existing_nid_jack->tag; + + /* + * Copy jack_detect from existing_nid_jack to avoid + * snd_hda_jack_detect_enable_callback_mst() making multiple + * SET_UNSOLICITED_ENABLE calls on the same pin. + */ + jack->jack_detect = existing_nid_jack->jack_detect; + } else { + jack->tag = codec->jacktbl.used; + } + //// use this to prevent AC_VERB_GET_PIN_SENSE (f09) verbs being sent - not seen in OSX logs + // well it does but it also makes the jack seem permanently connected + // now going with Dell style fixup + //jack->phantom_jack = 1; + return jack; +} + +// another copied routine as this is local to hda_jack.c +static struct hda_jack_callback * +cs_8409_find_callback_from_list(struct hda_jack_tbl *jack, + hda_jack_callback_fn func) +{ + struct hda_jack_callback *cb; + + if (!func) + return NULL; + + for (cb = jack->callback; cb; cb = cb->next) { + if (cb->func == func) + return cb; + } + + return NULL; +} + +// quick debug callback list function +void cs_8409_dump_callback(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + struct hda_jack_callback *cb; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + for (cb = jack->callback; cb; cb = cb->next) { + printk("snd_hda_intel: cs_8409_dump_callback jack num %d nid 0x%02x tag 0x%08x func %pF\n",i,jack->nid,jack->tag,cb->func); + } + } + + return; +} + +// copy of snd_hda_jack_detect_enable_callback code so doesnt send AC_VERB_SET_UNSOLICITED_ENABLE +// for Apple there are no AC_VERB_SET_UNSOLICITED_ENABLE verbs sent for 8409 +// it appears unsolicited response is pre-enabled +// but we need to fix this to setup the callback on such responses +// note that the current (>5.13) callback does not have a tag argument +struct hda_jack_callback * +cs_8409_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, int dev_id, int tag, + hda_jack_callback_fn func) +{ + struct hda_jack_tbl *jack; + struct hda_jack_callback *callback = NULL; + int err; + + myprintk("snd_hda_intel: cs_8409_hda_jack_detect_enable_callback nid 0x%02x dev_id %d tag 0x%02x\n", nid, dev_id, tag); + + jack = cs_8409_hda_jack_tbl_new(codec, nid, dev_id); + if (!jack) + return ERR_PTR(-ENOMEM); + + callback = cs_8409_find_callback_from_list(jack, func); + + if (func && !callback) { + callback = kzalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) + return ERR_PTR(-ENOMEM); + callback->func = func; + callback->nid = jack->nid; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + callback->dev_id = jack->dev_id; +#endif + callback->next = jack->callback; + jack->callback = callback; + } + + if (jack->jack_detect) + return callback; /* already registered */ + jack->jack_detect = 1; + // update the tag - linux code just counted the number of jacks set up + // for a tag - now done in cs_8409_hda_jack_tbl_new + // DANGEROUS - we do NOT check for unique tag here!! + if (tag != 0) + jack->tag = tag; + if (codec->jackpoll_interval > 0) + return callback; /* No unsol if we're polling instead */ + // apparently we dont need to send this + //err = snd_hda_codec_write_cache(codec, nid, 0, + // AC_VERB_SET_UNSOLICITED_ENABLE, + // AC_USRSP_EN | jack->tag); + //if (err < 0) + // return ERR_PTR(err); + return callback; +} + +// this is a copy of local routine call_jack_callback from hda_jack.c +static void cs_8409_apple_call_jack_callback(struct hda_codec *codec, unsigned int res, + struct hda_jack_tbl *jack) +{ + struct hda_jack_callback *cb; + + for (cb = jack->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; + cb->func(codec, cb); + } + if (jack->gated_jack) { + struct hda_jack_tbl *gated = +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, jack->dev_id); +#else + snd_hda_jack_tbl_get(codec, jack->gated_jack); +#endif + if (gated) { + for (cb = gated->callback; cb; cb = cb->next) { + cb->jack = jack; + cb->unsol_res = res; + cb->func(codec, cb); + } + } + } +} + + +#ifdef APPLE_CHANNEL_MAP +// new attempt to solve the channel map issue +static const struct snd_pcm_chmap_elem cs_8409_chmap[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_RL, + SNDRV_CHMAP_FR, SNDRV_CHMAP_RR } }, + { } +}; + +// this adds controls which I have no idea what they do +static void cs_8409_add_chmap_ctls(struct hda_codec *codec) +{ + int err = 0; + struct hda_pcm *pcm; + + mycodec_dbg(codec, "cs_8409_add_chmap_ctls enter"); + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct hda_pcm_stream *hinfo = + &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + struct snd_pcm_chmap *chmap; + const struct snd_pcm_chmap_elem *elem; + + if (pcm != NULL) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm name %s", pcm->name); + + if (hinfo != NULL) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo OK"); + elem = cs_8409_chmap; + if (hinfo->channels_max == 4) { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo chan == 4"); + err = snd_pcm_add_chmap_ctls(pcm->pcm, + SNDRV_PCM_STREAM_PLAYBACK, + elem, hinfo->channels_max, 0, &chmap); + if (err < 0) + mycodec_dbg(codec, "cs_8409_add_chmap_ctls failed!"); + } + } else { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm hinfo NULL"); + } + } else { + mycodec_dbg(codec, "cs_8409_add_chmap_ctls pcm NULL"); + } + } + + mycodec_dbg(codec, "cs_8409_add_chmap_ctls exit"); +} +#endif + +#ifdef ADD_EXTENDED_VERB +static void cs_8409_set_extended_codec_verb(void); +#endif + + +static void cs_8409_dump_auto_config(struct hda_codec *codec, const char *label_string) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + int itm = 0; + + myprintk("snd_hda_intel: dump %s\n", label_string); + + // dump headphone config + myprintk("snd_hda_intel: headphone config hp_jack_present %d\n",spec->gen.hp_jack_present); + myprintk("snd_hda_intel: headphone config line_jack_present %d\n",spec->gen.line_jack_present); + myprintk("snd_hda_intel: headphone config speaker_muted %d\n",spec->gen.speaker_muted); + myprintk("snd_hda_intel: headphone config line_out_muted %d\n",spec->gen.line_out_muted); + myprintk("snd_hda_intel: headphone config auto_mic %d\n",spec->gen.auto_mic); + myprintk("snd_hda_intel: headphone config automute_speaker %d\n",spec->gen.automute_speaker); + myprintk("snd_hda_intel: headphone config automute_lo %d\n",spec->gen.automute_lo); + myprintk("snd_hda_intel: headphone config detect_hp %d\n",spec->gen.detect_hp); + myprintk("snd_hda_intel: headphone config detect_lo %d\n",spec->gen.detect_lo); + myprintk("snd_hda_intel: headphone config keep_vref_in_automute %d\n",spec->gen.keep_vref_in_automute); + myprintk("snd_hda_intel: headphone config line_in_auto_switch %d\n",spec->gen.line_in_auto_switch); + myprintk("snd_hda_intel: headphone config auto_mute_via_amp %d\n",spec->gen.auto_mute_via_amp); + myprintk("snd_hda_intel: headphone config suppress_auto_mute %d\n",spec->gen.suppress_auto_mute); + myprintk("snd_hda_intel: headphone config suppress_auto_mic %d\n",spec->gen.suppress_auto_mic); + + myprintk("snd_hda_intel: headphone config hp_mic %d\n",spec->gen.hp_mic); + + myprintk("snd_hda_intel: headphone config suppress_hp_mic_detect %d\n",spec->gen.suppress_hp_mic_detect); + + + myprintk("snd_hda_intel: auto config pins line_outs %d\n", spec->gen.autocfg.line_outs); + myprintk("snd_hda_intel: auto config pins line_outs 0x%02x\n", spec->gen.autocfg.line_out_pins[0]); + myprintk("snd_hda_intel: auto config pins line_outs 0x%02x\n", spec->gen.autocfg.line_out_pins[1]); + myprintk("snd_hda_intel: auto config pins speaker_outs %d\n", spec->gen.autocfg.speaker_outs); + myprintk("snd_hda_intel: auto config pins speaker_outs 0x%02x\n", spec->gen.autocfg.speaker_pins[0]); + myprintk("snd_hda_intel: auto config pins speaker_outs 0x%02x\n", spec->gen.autocfg.speaker_pins[1]); + myprintk("snd_hda_intel: auto config pins hp_outs %d\n", spec->gen.autocfg.hp_outs); + myprintk("snd_hda_intel: auto config pins hp_outs 0x%02x\n", spec->gen.autocfg.hp_pins[0]); + myprintk("snd_hda_intel: auto config pins inputs %d\n", spec->gen.autocfg.num_inputs); + + for (itm = 0; itm < spec->gen.autocfg.num_inputs; itm++) { + + myprintk("snd_hda_intel: auto config pins inputs pin 0x%02x\n", spec->gen.autocfg.inputs[itm].pin); + myprintk("snd_hda_intel: auto config pins inputs type %d\n", spec->gen.autocfg.inputs[itm].type); + myprintk("snd_hda_intel: auto config pins inputs is head set mic %d\n", spec->gen.autocfg.inputs[itm].is_headset_mic); + myprintk("snd_hda_intel: auto config pins inputs is head phn mic %d\n", spec->gen.autocfg.inputs[itm].is_headphone_mic); + myprintk("snd_hda_intel: auto config pins inputs is boost %d\n", spec->gen.autocfg.inputs[itm].has_boost_on_pin); + } + + myprintk("snd_hda_intel: auto config inputs num_adc_nids %d\n", spec->gen.num_adc_nids); + for (itm = 0; itm < spec->gen.num_adc_nids; itm++) { + myprintk("snd_hda_intel: auto config inputs adc_nids 0x%02x\n", spec->gen.adc_nids[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is num_dacs %d\n", spec->gen.multiout.num_dacs); + for (itm = 0; itm < spec->gen.multiout.num_dacs; itm++) { + myprintk("snd_hda_intel: auto config multiout is dac_nids 0x%02x\n", spec->gen.multiout.dac_nids[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is hp_nid 0x%02x\n", spec->gen.multiout.hp_nid); + + for (itm = 0; itm < ARRAY_SIZE(spec->gen.multiout.hp_out_nid); itm++) { + if (spec->gen.multiout.hp_out_nid[itm]) + myprintk("snd_hda_intel: auto config multiout is hp_out_nid 0x%02x\n", spec->gen.multiout.hp_out_nid[itm]); + } + for (itm = 0; itm < ARRAY_SIZE(spec->gen.multiout.extra_out_nid); itm++) { + if (spec->gen.multiout.extra_out_nid[itm]) + myprintk("snd_hda_intel: auto config multiout is xtr_out_nid 0x%02x\n", spec->gen.multiout.extra_out_nid[itm]); + } + + myprintk("snd_hda_intel: auto config multiout is dig_out_nid 0x%02x\n", spec->gen.multiout.dig_out_nid); + myprintk("snd_hda_intel: auto config multiout is slv_dig_out %p\n", spec->gen.multiout.follower_dig_outs); + +} + + +static void cs_8409_dump_path(struct nid_path *path) +{ + int i; + int j; + + myprintk("snd_hda_intel: dump path\n"); + + myprintk("snd_hda_intel: path %d: active %d pin_enabled %d pin_fixed %d stream_enabled %d\n", i, path->active, path->pin_enabled, path->pin_fixed, path->stream_enabled); + myprintk("snd_hda_intel: path %d: depth %d\n", i, path->depth); + for(j=0; j < path->depth; j++) { + myprintk("snd_hda_intel: path %d: path nid 0x%02x idx %d multi %d\n", j, path->path[j], path->idx[j], path->multi[j]); + } + for(j=0; j < NID_PATH_NUM_CTLS; j++) { + myprintk("snd_hda_intel: ctl %d: path 0x%08x\n", j, path->ctls[j]); + } + + myprintk("snd_hda_intel: dump path end\n"); +} + + +// so now think multi in the path is different from multiout +// - now think its about if there are multiple connections as listed by AC_VERB_GET_CONNECT_LIST +// - still havent figured out idx tho + +static void cs_8409_dump_paths(struct hda_codec *codec, const char *label_string) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + const struct nid_path *path; + int i; + int j; + + myprintk("snd_hda_intel: dump paths %s\n", label_string); + + snd_array_for_each(&spec->gen.paths, i, path) { + myprintk("snd_hda_intel: path %d: active %d pin_enabled %d pin_fixed %d stream_enabled %d\n", i, path->active, path->pin_enabled, path->pin_fixed, path->stream_enabled); + myprintk("snd_hda_intel: path %d: depth %d\n", i, path->depth); + for(j=0; j < path->depth; j++) { + myprintk("snd_hda_intel: path %d: path nid 0x%02x idx %d multi %d\n", j, path->path[j], path->idx[j], path->multi[j]); + } + for(j=0; j < NID_PATH_NUM_CTLS; j++) { + myprintk("snd_hda_intel: ctl %d: path 0x%08x\n", j, path->ctls[j]); + } + + } + + myprintk("snd_hda_intel: dump paths end\n"); +} + + + +// renamed this function as we dont want it called on resume +// because we are using an explicit version of build controls we can add it there + +static int cs_8409_apple_boot_init(struct hda_codec *codec) +{ + struct hda_pcm *info = NULL; + struct hda_pcm_stream *hinfo = NULL; + struct cs8409_apple_spec *spec = NULL; + //struct snd_kcontrol *kctl = NULL; + int pcmcnt = 0; + int ret_unsol_enable = 0; + + // so apparently if we do not define a resume function + // then this init function will be called on resume + // is that what we want here?? + // NOTE this is called for either playback or capture + + myprintk("snd_hda_intel: cs_8409_apple_boot_init\n"); + + //dump_stack(); + + + // NOTE that this function is called after the build pcm functions + + + // dump the rates/format of the afg node + // so analog_playback_stream is still NULL here - maybe only defined when doing actual playback + // the info stream is now defined + spec = codec->spec; + hinfo = spec->gen.stream_analog_playback; + if (hinfo != NULL) + { + mycodec_dbg(codec, "hinfo stream nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + } + else + mycodec_dbg(codec, "hinfo stream NULL\n"); + + // think this is what I need to fixup + + list_for_each_entry(info, &codec->pcm_list_head, list) { + int stream; + + mycodec_dbg(codec, "cs_8409_apple_boot_init pcm %d\n",pcmcnt); + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *hinfo = &info->stream[stream]; + + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d pointer %p\n",stream,hinfo); + + if (hinfo != NULL) + { + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d nid 0x%02x rates 0x%08x formats 0x%016llx\n",stream,hinfo->nid,hinfo->rates,hinfo->formats); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream substreams %d\n",hinfo->substreams); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream channels min %d\n",hinfo->channels_min); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream channels max %d\n",hinfo->channels_max); + mycodec_dbg(codec, "cs_8409_apple_boot_init stream maxbps %d\n",hinfo->maxbps); + } + else + mycodec_dbg(codec, "cs_8409_apple_boot_init info stream %d NULL\n", stream); + } + pcmcnt++; + } + + // update the streams specifically by nid + // we seem to have only 1 stream here with the nid of 0x02 + // (I still dont really understand the linux generic coding here) + // with capture devices we seem to get 2 pcm streams (0 and 1) + // each pcm stream has an output stream (0) and an input stream (1) + // the 1st pcm stream (0) is assigned nid 0x02 for output and nid 0x22 (macbook pro) for input (internal mike) + // the 2nd pcm stream (1) has a dummy output stream and nid 0x1a for input (headset mike via cs42l83) + // (NOTE this means the line input stream (0x45->0x23) (macbook pro) is not assigned currently ie not useable) + + list_for_each_entry(info, &codec->pcm_list_head, list) { + int stream; + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *hinfo = &info->stream[stream]; + + if (hinfo != NULL) + { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + { + if (hinfo->nid == 0x02) + { + mycodec_dbg(codec, "cs_8409_apple_boot_init info playback stream %d pointer %p\n",stream,hinfo); + // so now we need to force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // probably can leave S32_LE + // we can still handle 2/4 channel (what about 1 channel?) + hinfo->rates = SNDRV_PCM_RATE_44100; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE; + mycodec_dbg(codec, "playback info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + + // update the playback function + hinfo->ops.prepare = cs_8409_playback_pcm_prepare; + } + } + else if (stream == SNDRV_PCM_STREAM_CAPTURE) + { + //if (hinfo->nid == 0x22) + if (hinfo->nid == spec->intmike_adc_nid) + { + // this is the internal mike + // this is a bit weird - the output nodes are id'ed by input pin nid + // but the input nodes are done by the input (adc) nid - not the input pin nid + mycodec_dbg(codec, "cs_8409_apple_boot_init info capture stream %d pointer %p\n",stream,hinfo); + // so now we could force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // but this internal mike seems to be a standard HDA input setup so we could have any format here + //hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE; + hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S24_3LE; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE; + //hinfo->maxbps = 24; + mycodec_dbg(codec, "capture info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx maxbps %d\n",hinfo->nid,hinfo->rates,hinfo->formats,hinfo->maxbps); + // update the capture function + hinfo->ops.prepare = cs_8409_capture_pcm_prepare; + } + else if (hinfo->nid == 0x1a) + { + // this is the external mike ie headset mike + // this is a bit weird - the output nodes are id'ed by input pin nid + // but the input nodes are done by the input (adc) nid - not the input pin nid + mycodec_dbg(codec, "cs_8409_apple_boot_init info capture stream %d pointer %p\n",stream,hinfo); + // so now we force the rates and formats to the single one Apple defines ie 44.1 kHz and S24_LE + // - because this format is the one being returned by the cs42l83 which is setup by undocumented i2c commands + hinfo->rates = SNDRV_PCM_RATE_44100; + //hinfo->formats = SNDRV_PCM_FMTBIT_S24_LE; + hinfo->formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE; + //hinfo->maxbps = 24; + mycodec_dbg(codec, "capture info stream forced nid 0x%02x rates 0x%08x formats 0x%016llx maxbps %d\n",hinfo->nid,hinfo->rates,hinfo->formats,hinfo->maxbps); + // update the capture function + hinfo->ops.prepare = cs_8409_capture_pcm_prepare; + } + // still not sure what we do about the linein nid + // is this bidirectional - because we have no lineout as far as I can see + } + } + else + mycodec_dbg(codec, "cs_8409_apple_boot_init info pcm stream %d NULL\n", stream); + } + pcmcnt++; + } + + + //list_for_each_entry(kctl, &codec->card->controls, list) { + //} + + +#ifdef ADD_EXTENDED_VERB + cs_8409_set_extended_codec_verb(); +#endif + + myprintk("snd_hda_intel: end cs_8409_apple_boot_init\n"); + + return 0; +} + + +static int cs_8409_apple_init(struct hda_codec *codec) +{ + struct hda_pcm *info = NULL; + struct hda_pcm_stream *hinfo = NULL; + struct cs8409_apple_spec *spec = NULL; + //struct snd_kcontrol *kctl = NULL; + int pcmcnt = 0; + int ret_unsol_enable = 0; + + // not sure what the init function is supposed to be doing + // its called in snd_hda_codec_build_controls + // but also from the default resume function hda_call_codec_resume + + // so apparently if we do not define a resume function + // then this init function will be called on resume + // is that what we want here?? + // NOTE this is called for either playback or capture + + myprintk("snd_hda_intel: cs_8409_apple_init\n"); + + //dump_stack(); + + + //if (spec->vendor_nid == CS420X_VENDOR_NID) { + // /* init_verb sequence for C0/C1/C2 errata*/ + // snd_hda_sequence_write(codec, cs_errata_init_verbs); + // snd_hda_sequence_write(codec, cs_coef_init_verbs); + //} else if (spec->vendor_nid == CS4208_VENDOR_NID) { + // snd_hda_sequence_write(codec, cs4208_coef_init_verbs); + //} + + + //// so it looks as tho we have an issue when using headsets + //// - because the 8409 is totally messed up it does not switch the inputs + //// when a headset is plugged in + //// not sure about this here - maybe move to where disable internal mike nodes + //if (spec->jack_present) { + //} + + + // so the following powers on all active nodes - but if we have just plugged + // in a headset thats still the internal mike and amps + + // commenting this as we dont need it + // it powers on the nids, plus updates the mute/volume + // via path_power_update and activate_amp_in or activate_amp_out + //snd_hda_gen_init(codec); + + + // read UNSOL enable data to see what initial setup is + //ret_unsol_enable = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x01 boot setup is 0x%08x\n",ret_unsol_enable); + //ret_unsol_enable = snd_hda_codec_read(codec, 0x47, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x47 boot setup is 0x%08x\n",ret_unsol_enable); + + + //if (spec->gpio_mask) { + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + // spec->gpio_mask); + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + // spec->gpio_dir); + // snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + // spec->gpio_data); + //} + + //if (spec->vendor_nid == CS420X_VENDOR_NID) { + // init_input_coef(codec); + // init_digital_coef(codec); + //} + + + myprintk("snd_hda_intel: end cs_8409_apple_init\n"); + + return 0; +} + +static int cs_8409_apple_resume(struct hda_codec *codec) +{ + myprintk("snd_hda_intel: cs_8409_apple_resume\n"); + // code copied from default resume patch ops + if (codec->patch_ops.init) + codec->patch_ops.init(codec); + snd_hda_regmap_sync(codec); + myprintk("snd_hda_intel: end cs_8409_apple_resume\n"); + return 0; +} + +static int cs_8409_apple_suspend(struct hda_codec *codec) +{ + myprintk("snd_hda_intel: cs_8409_apple_suspend\n"); + // no additional code for this so just dummy it + return 0; +} + +static int cs_8409_apple_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + myprintk("snd_hda_intel: cs_8409_apple_check_power_status\n"); + // no additional code for this so just dummy it + return 0; +} + + +static int cs_8409_apple_build_controls(struct hda_codec *codec) +{ + int err; + + myprintk("snd_hda_intel: cs_8409_apple_build_controls\n"); + //dump_stack(); + + + + // try here - its mainly dumping the pcms which have been built by now + cs_8409_apple_boot_init(codec); + + // so if we have paths (active??) then snd_hda_gen_build_controls will add the end point + // (ie adc input nid or output nid (often dac but just pass through for 8409) as jacks + // if the nid has no PIN_SENSE then these will be phantom jacks (which means treated as always plugged in) + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + + // this seems better as snd_hda_gen_build_controls does an hda_exec_init_verbs call + // before calling the init patch_ops function + // - on the other hand currently it is just dumping data doesnt actually do anything + //cs_8409_apple_boot_init(codec); + + + // before or after the snd_hda_apply_fixup?? + //cs_8409_add_chmap_ctls(codec); + + myprintk("snd_hda_intel: end cs_8409_apple_build_controls\n"); + return 0; +} + +int cs_8409_apple_build_pcms(struct hda_codec *codec) +{ + int retval; + struct hda_pcm *pcm; + //struct cs8409_apple_spec *spec = codec->spec; + //struct hda_pcm *info = NULL; + //struct hda_pcm_stream *hinfo = NULL; + + + myprintk("snd_hda_intel: cs_8409_apple_build_pcms\n"); + + + // so I dont get how this leads to the observed controls in pulse/alsa + // so far snd_hda_gen_build_pcms seems to add channel maps of stereo and 2.1 + // - cant figure out where the surround is generated from + // (also cant see any way to disable the 2.1 without explicit recoding) + + + // to replace this with explicit code would require a lot of static function copying + // from hda_generic.c (and maybe hda_codec.c) + // current issue is the default code will add a 2.1 chmap for 4 channels - I want to remove that + retval = snd_hda_gen_build_pcms(codec); + + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct snd_pcm_chmap *chmap; + //const struct snd_pcm_chmap_elem *elem; + if (pcm != NULL) { + myprintk("snd_hda_intel: cs_8409_apple_build_pcms name %s\n", pcm->name); + if (pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap != NULL) { + const struct snd_pcm_chmap_elem *elem; + elem = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap[0]; + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap chnls %d map %d %d\n", elem->channels, elem->map[0], elem->map[1]); + elem = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap[1]; + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap chnls %d map %d %d %d %d\n", elem->channels, elem->map[0], elem->map[1], elem->map[2], elem->map[3]); + } else { + myprintk("snd_hda_intel: cs_8409_apple_build_pcms chmap NULL\n"); + } + // if (pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max == 4) { + // myprintk("snd_hda_intel: cs_8409_apple_build_pcms - adding chmap OK\n"); + // pcm->own_chmap = true; + // pcm->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = cs_8409_chmap; + // } + } + } + + // we still dont have the pcm streams defined by here + // ah this is all done in snd_hda_codec_build_pcms + // which calls this patch routine or snd_hda_gen_build_pcms + // but the query supported pcms is only done after this + myprintk("snd_hda_intel: end cs_8409_apple_build_pcms\n"); + return retval; +} + + +// copy of cs8409_fix_caps with debug prints +// - rather than adding the prints to the patch_cs8409.c routine +static void cs8409_fix_caps_debug(struct hda_codec *codec, unsigned int nid) +{ + int caps; + + /* CS8409 is simple HDA bridge and intended to be used with a remote + * companion codec. Most of input/output PIN(s) have only basic + * capabilities. Receive and Transmit NID(s) have only OUTC and INC + * capabilities and no presence detect capable (PDC) and call to + * snd_hda_gen_build_controls() will mark them as non detectable + * phantom jacks. However, a companion codec may be + * connected to these pins which supports jack detect + * capabilities. We have to override pin capabilities, + * otherwise they will not be created as input devices. + */ + caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); + if (caps >= 0) + snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, + (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT))); + + myprintk("snd_hda_intel: cs8409_fix_caps nid 0x%02x pcap 0x%08x : 0x%08x 0x%08x 0x%08x\n",nid,caps,AC_PINCAP_IMP_SENSE,AC_PINCAP_PRES_DETECT,AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT); + myprintk("snd_hda_intel: cs8409_fix_caps nid 0x%02x wcap 0x%08x : 0x%08x\n",nid,get_wcaps(codec, nid),AC_WCAP_UNSOL_CAP); + + snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP)); +} + + + +// set up some local routines we can call in our main code to call the hda functions we need + +void cs_8409_cs42l83_mark_jack(struct hda_codec *codec) +{ + struct hda_jack_tbl *event; + + mycodec_info(codec, "cs_8409_cs42l83_mark_jack\n"); + + // this is how the Dell fixups do it + // first explicitly get the jacktbl entry using the known jack nids + // so can create the res data tag to pass to the standard snd_hda_jack_unsol_event + // (minus actual GPIO etc data usually seen in the res) + // - which then scans the jacktbl for the tag etc + // but primarily marks the jack as dirty prior to snd_hda_jack_report_sync call + // plus calls the jack callbacks + + event = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L83_HP_PIN_NID, 0); + + // note that the only real functions of snd_hda_jack_unsol_event are to call the jack callbacks + // and snd_hda_jack_report_sync + // for Apple we have disabled power on/power off callbacks (power_save_node = 0) + // so now only have call_hp_automute and call_mic_autoswitch to figure out + // - currently call_hp_automute is disabled because of suppress_auto_mute + + if (event) { + // now perform the functions of snd_hda_jack_unsol_event explicitly + //snd_hda_jack_unsol_event(codec, (event->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); + // ignoring key_report_jack at the moment + event->jack_dirty = 1; + //cs_8409_apple_call_jack_callback(codec, res, event); + //snd_hda_jack_report_sync(codec); + } + + // note that the Dell fixups do calls to snd_hda_jack_unsol_event for each jack nid + // so we have multiple calls to snd_hda_jack_report_sync + // so far looks as though this is OK as eventually snd_hda_jack_report_sync calls snd_kctl_jack_report + // to update user side which first checks if stored data is same as updated data and if so does nothing + //snd_hda_jack_report_sync(codec); + + mycodec_info(codec, "end cs_8409_cs42l83_mark_jack\n"); +} + + +void cs_8409_cs42l83_jack_report_sync(struct hda_codec *codec) +{ + mycodec_info(codec, "cs_8409_cs42l83_jack_report_sync\n"); + snd_hda_jack_report_sync(codec); +} + + +// this function is for updating jacks when headphone is plugged in or unplugged +void cs_8409_cs42l83_jack_report_hp_update(struct hda_codec *codec, int plugin) +{ + mycodec_info(codec, "cs_8409_cs42l83_jack_report_hp_update: %s\n", (plugin?"plugin":"unplug")); + + snd_hda_jack_report_sync(codec); +} + + +static void cs_8409_cs42l83_unsol_event_handler(struct hda_codec *codec, unsigned int unsol_res); + +// so I think this is what gets called for any unsolicited event - including jack plug events +// so anything we do to switch amp/headphone should be done from here + +void cs_8409_cs42l83_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct hda_jack_tbl *event; + //int ret_unsol_enable = 0; + //int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f; + int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT; + + // so we have confirmed that these unsol responses are not in linux kernel interrupt state + //if (in_interrupt()) + // mycodec_info(codec, "cs_8409_cs42l83_unsol_event - INTERRUPT\n"); + //else + // mycodec_info(codec, "cs_8409_cs42l83_unsol_event - not interrupt\n"); + + //// read UNSOL enable data to see what current setup is + //ret_unsol_enable = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x01 at unsol is 0x%08x\n",ret_unsol_enable); + //ret_unsol_enable = snd_hda_codec_read(codec, 0x47, 0, AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + //mycodec_dbg(codec,"UNSOL event 0x47 at unsol is 0x%08x\n",ret_unsol_enable); + + // so it seems the low order byte of the res for the 8409 is a copy of the GPIO register state + //// - except that we dont seem to pass this to the callback functions!! - well that was for linux kernel version 4 + + codec_info(codec, "cs_8409_cs42l83_jack_unsol_event UNSOL 0x%08x tag 0x%02x\n",res,tag); + + + // so now think we dont follow snd_hda_jack_unsol_event - that assumes multiple jacks (nids) from which we have + // to find one from the tag - hence the use of the jacbktbl.used count for the tag + // for Apple we have one tag because we have one jack and multiple nids + // hda_generic implements a gated jack which maybe for handling the case of + // 2 nids per jack (one for headphones and one for head mike) + // (see path_realtek.c which seems to use that for hp nids with mic nids) + // for the apple case even more problematic as we have more than 2 nids, one for headphone output, one for + // headset input and one for linein input (currently have no idea how to handle lineout) all in same jack + + // so far it appears Apple enables speakers and disables intmike and linein with no headphone + // intmike only enabled if perform capture + // dont have any linein enable examples + + // now updating as per the Dell module - which looks up each jacktbl entry explicitly + // not sure where to assign the jacktbl/event + // standard snd_hda_jack_unsol_event does the lookup by tag here + // Dell does it in the interrupt handler for the subcodec by reading/writing to the subcodec + // it now seems we have only one jack nid to worry about so could find it here + // currently going with doing it in cs_8409_cs42l83_mark_jack which we will call + // from the appropriate place as called by the cs_8409_cs42l83_unsol_event_handler call chain + + + + // this likely was for kernel version 4 - all kernel 5 versions seem to pass res to call_jack_callback + //// its the callback struct thats passed as an argument to the callback function + //// so stuff the res data in the private_data member which seems to be used for such a purpose + ////event->callback->private_data = res; + + // leave this as is even tho so far have only 1 tag so not really needed + // so could just call the callback routine directly here + // now removing this - the main callbacks are power up, power down and mic_autoswitch + // at the moment we are ignoring any power up/down calls - everything is permanently powered on + // we still have to deal with mic_autoswitch + // we could do this in the new cs_8409_cs42l83_mark_jack function + //cs_8409_apple_call_jack_callback(codec, res, event); + + + cs_8409_cs42l83_unsol_event_handler(codec, res); + + + // we seem to get a weird UNSOL interrupt during initialization - we want to skip the following + if (((struct cs8409_spec*)(codec->spec))->headset_phase == 0) + return; + + // this is the code that generates the AC_VERB_GET_PIN_SENSE (0xf09) verb + // however if we define the jack as a phantom_jack we do not send the AC_VERB_GET_PIN_SENSE (0xf09) verb + + // NOTA BENE - this is a very significant routine which notifies user space of the + // jack plug/unplug event + // however now think we need to move the actual call to when we know we have a jack + // (as per the Dell code to after the cs42l83 headphone sense) + //snd_hda_jack_report_sync(codec); +} + +// Im pretty convinced that Apple uses a timed event from the plugin event +// before performing further setup +// not clear how to set this up in linux +// timer might be way to go but there are some limitations on the timer function +// which is not clear is going to work here +// now think just using msleeps is the way to go - this is similar to code in patch_realtek.c +// for dealing with similar issues +//static struct timer_list cs_8409_hp_timer; + +//static void cs_8409_hp_timer_callback(struct timer_list *tlist) +//{ +// myprintk("snd_hda_intel: cs_8409_hp_timer_callback\n"); +//} + +// have an explict one for 8409 +// cs_free is just a definition +//#define cs_8409_apple_free snd_hda_gen_free + +void cs_8409_apple_free(struct hda_codec *codec) +{ +#if 0 + struct cs8409_spec *spec = codec->spec; + + /* Cancel i2c clock disable timer, and disable clock if left enabled */ + cancel_delayed_work_sync(&spec->i2c_clk_work); + cs8409_disable_i2c_clock(codec); +#endif + + //del_timer(&cs_8409_hp_timer); + + snd_hda_gen_free(codec); +} + + +// note this must come after any function definitions used + +static const struct hda_codec_ops cs_8409_apple_patch_ops = { + .build_controls = cs_8409_apple_build_controls, + .build_pcms = cs_8409_apple_build_pcms, + .init = cs_8409_apple_init, + .free = cs_8409_apple_free, + .unsol_event = cs_8409_cs42l83_jack_unsol_event, +#ifdef CONFIG_PM + .resume = cs_8409_apple_resume, + .suspend = cs_8409_apple_suspend, + .check_power_status = cs_8409_apple_check_power_status, +#endif +}; + + + +// jack handling analysis +// now it appears that unsolicited events are assumed to be due to jack plug/unplug events +// so the .unsol_event function is the primary handling function for this +// and this does NOT appear to depend on PIN_SENSE capability +// the PIN_SENSE capability seems to be ONLY needed for automute functionality +// this includes enabling/disabling whether we see a Headphone entry in the settings sound dialog + + +static int cs_8409_apple_create_input_ctls(struct hda_codec *codec); + +static void cs_8409_cs42l83_callback(struct hda_codec *codec, struct hda_jack_callback *event); + + +static int cs_8409_apple_parse_auto_config(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + int err; + int i; + + myprintk("snd_hda_intel: cs_8409_apple_parse_auto_config\n"); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + // dump the actual auto config before mess with it + cs_8409_dump_auto_config(codec, "post snd_hda_gen_parse_auto_config"); + + + // try and dump the nid paths to sort out how to set them + debug_show_configs(codec, &spec->gen.autocfg); + + + // dump the raw path storage + cs_8409_dump_paths(codec, "post auto config"); + + + // this would be done in snd_hda_gen_parse_auto_config by check_auto_mute_availability + // (also by pin power setup) + // so now think we should NOT do this - we would need to add this per jack nid but we only + // want to call cs_8409_cs42l83_callback once per unsol event + //cs_8409_hda_jack_detect_enable_callback(codec, 0x01, 0, 0x37, cs_8409_cs42l83_callback); + + // debug dump of codec jack table callback functions +#ifdef MYSOUNDDEBUGFULL + cs_8409_dump_callback(codec); +#else +#ifdef MYSOUNDDEBUG + cs_8409_dump_callback(codec); +#endif +#endif + + // note that create_input_ctls is called towards the end of snd_hda_gen_parse_auto_config + + // it appears the auto config assumes that inputs are connected to ADCs + // (not true for outputs) + + // according to the Dell version the issue is the 8409 nids have no pin sense capabilities + // and they get set to phantom jacks - the Dell version fixes the 8409 inputs/outputs connected to the cs42l42 companion codec + // with PIN SENSE and DETECT capabilities (cs8409_fix_caps) then uses the cs8409_cs42l42_exec_verb replacement + // for the primary spec->exec_verb function to update the returns from the AC_VERB_GET_PIN_SENSE verb for those + // nids with actual state from the companion codec + // note this has no bearing on the auto config - its for handling the unsol events properly + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + // new routine to setup inputs - based on the hda_generic code + cs_8409_apple_create_input_ctls(codec); +#else + // as of 5.13 the definition of AUTO_CFG_MAX_INS has been increased to handle the 8409 + // so the above may not be needed + // (without the above no input pins were recognised at all) + // need to check if fixed other input definitions in cs_8409_apple_create_input_ctls + // and redo the updated input definitions here +#endif + + + // so do I keep this or not?? + /* keep the ADCs powered up when it's dynamically switchable */ + if (spec->gen.dyn_adc_switch) { + unsigned int done = 0; + for (i = 0; i < spec->gen.input_mux.num_items; i++) { + int idx = spec->gen.dyn_adc_idx[i]; + if (done & (1 << idx)) + continue; + snd_hda_gen_fix_pin_power(codec, + spec->gen.adc_nids[idx]); + done |= 1 << idx; + } + } + + myprintk("snd_hda_intel: end cs_8409_apple_parse_auto_config\n"); + + return 0; +} + +// this is only needed if kernel is < 5.13 +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + +// pigs - we need a lot of hda_generic local functions +#include "patch_cirrus_hda_generic_copy.h" + +// so we need to hack this code because we have more adcs than AUTO_CFG_MAX_INS +// adcs (8) - actual number is 18 +// no good way to do this - except to check connection list for each adc and +// see if connected to nid we are looking at +// so define new function + +static int cs_8409_add_adc_nid(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int nums = 0; + int itm = 0; + + myprintk("snd_hda_intel: cs_8409_add_adc_nid pin 0x%x\n",pin); + + for_each_hda_codec_node(nid, codec) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + int fndnid = 0; + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + + //myprintk("snd_hda_intel: cs_8409_add_adc_nid nid 0x%x\n",nid); + + { + const hda_nid_t *connptr = NULL; + int num_conns = snd_hda_get_conn_list(codec, nid, &connptr); + int i; + fndnid = 0; + for (i = 0; i < num_conns; i++) { + //myprintk("snd_hda_intel: cs_8409_add_adc_nid %d 0x%x\n",num_conns,connptr[i]); + if (connptr[i] == pin) { + fndnid = nid; + } + } + } + if (fndnid == 0) + continue; + + // save only 1st one we match + if (spec->num_adc_nids+1 >= max_nums) + break; + adc_nids[spec->num_adc_nids] = nid; + spec->num_adc_nids += 1; + break; + } + + + mycodec_dbg(codec, "snd_hda_intel: cs_8409_add_adc_nid num nids %d\n",nums); + + for (itm = 0; itm < spec->num_adc_nids; itm++) { + myprintk("snd_hda_intel: cs_8409_add_adc_nid 0x%02x\n", spec->adc_nids[itm]); + } + + myprintk("snd_hda_intel: end cs_8409_add_adc_nid\n"); + + return nums; +} + + + +// copied from parse_capture_source in hda_generic.c +// we need this although not changed (apart from printks) because local to hda_generic.c + +/* parse capture source paths from the given pin and create imux items */ +static int cs_8409_parse_capture_source(struct hda_codec *codec, hda_nid_t pin, + int cfg_idx, int num_adcs, + const char *label, int anchor) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int imux_idx = imux->num_items; + bool imux_added = false; + int c; + + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x\n",pin); + + for (c = 0; c < num_adcs; c++) { + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x adc 0x%x check reachable\n",pin,adc); + + if (!is_reachable_path(codec, pin, adc)) + continue; + myprintk("snd_hda_intel: cs_8409_parse_capture_source pin 0x%x adc 0x%x reachable\n",pin,adc); + path = snd_hda_add_new_path(codec, pin, adc, anchor); + if (!path) + continue; + print_nid_path(codec, "input", path); + spec->input_paths[imux_idx][c] = + snd_hda_get_path_idx(codec, path); + + if (!imux_added) { + if (spec->hp_mic_pin == pin) + spec->hp_mic_mux_idx = imux->num_items; + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL); + imux_added = true; + if (spec->dyn_adc_switch) + spec->dyn_adc_idx[imux_idx] = c; + } + } + + myprintk("snd_hda_intel: end cs_8409_parse_capture_source\n"); + + return 0; +} + + +#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ + +// copied from create_input_ctls in hda_generic.c + +static int cs_8409_apple_create_input_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; + int num_adcs = 0; + int i, err; + unsigned int val; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls\n"); + + // we cannot do this + //num_adcs = cs_8409_fill_adc_nids(codec); + //if (num_adcs < 0) + // return 0; + + // clear out the auto config setup + // hope that all_adcs is not different from adc_nids - doesnt seem to be for auto config only + memset(spec->adc_nids, 0, sizeof(spec->adc_nids)); + memset(spec->all_adcs, 0, sizeof(spec->all_adcs)); + spec->num_adc_nids = 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + int fndadc = 0; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d\n",i); + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x\n",i,pin); + + // now scan all nodes for adc nodes and find one connected to this pin + fndadc = cs_8409_add_adc_nid(codec, pin); + if (!fndadc) + continue; + } + + num_adcs = spec->num_adc_nids; + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = spec->num_adc_nids; + memcpy(spec->all_adcs, spec->adc_nids, spec->num_adc_nids* sizeof(hda_nid_t)); + + err = fill_input_pin_labels(codec); + if (err < 0) + return err; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + int fndadc = 0; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d\n",i); + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x\n",i,pin); + + //// now scan the adc nodes and find one connected to this pin + //fndadc = cs_8409_add_adc_nid(codec, pin); + //if (!fndadc) + // continue; + + val = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, pin); + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x val 0x%x\n",i,pin,val); + + if (pin != spec->hp_mic_pin && + !snd_hda_codec_get_pin_target(codec, pin)) + set_pin_target(codec, pin, val, false); + + myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls - input %d pin 0x%x val 0x%x mixer 0x%x\n",i,pin,val,mixer); + + if (mixer) { + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, i, pin, + spec->input_labels[i], + spec->input_label_idxs[i], + mixer); + if (err < 0) + return err; + } + } + + // so connections are from the adc nid to the input pin nid + //{ + //const hda_nid_t conn[256]; + //const hda_nid_t *connptr = conn; + //int num_conns = snd_hda_get_conn_list(codec, pin, &connptr); + //int i; + //myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls pin 0x%x num conn %d\n",pin,num_conns); + //for (i = 0; i < num_conns; i++) { + // myprintk("snd_hda_intel: cs_8409_apple_create_input_ctls pin 0x%x conn 0x%x\n",pin,conn[i]); + //} + //} + + + // this is the problem routine - this loops over the adcs to do anything + // so if num_adcs is 0 or none of the adc entries are used this does nothing + + err = cs_8409_parse_capture_source(codec, pin, i, num_adcs, + spec->input_labels[i], -mixer); + if (err < 0) + return err; + + // comment for the moment as needs lots of other functions + //if (spec->add_jack_modes) { + // err = create_in_jack_mode(codec, pin); + // if (err < 0) + // return err; + //} + } + + /* add stereo mix when explicitly enabled via hint */ + if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { + err = cs_8409_parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, + "Stereo Mix", 0); + if (err < 0) + return err; + else + spec->suppress_auto_mic = 1; + } + + myprintk("snd_hda_intel: end cs_8409_apple_create_input_ctls\n"); + + return 0; +} + +#endif + +/* do I need this for 8409 - I certainly need some gpio patching */ +static void cs_8409_apple_fixup_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio\n"); + + // allowable states + // HDA_FIXUP_ACT_PRE_PROBE, + // HDA_FIXUP_ACT_PROBE, + // HDA_FIXUP_ACT_INIT, + // HDA_FIXUP_ACT_BUILD, + // HDA_FIXUP_ACT_FREE, + + // so inspection suggests no eapd usage on macs - no 0xf0c or 0x70c commands sent + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + //struct cs8409_apple_spec *spec = codec->spec; + + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio pre probe\n"); + + //myprintk("fixup gpio hp=0x%x speaker=0x%x\n", hp_out_mask, speaker_out_mask); + //spec->gpio_eapd_hp = hp_out_mask; + //spec->gpio_eapd_speaker = speaker_out_mask; + //spec->gpio_mask = 0xff; + //spec->gpio_data = + // spec->gpio_dir = + // spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } + else if (action == HDA_FIXUP_ACT_PROBE) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio probe\n"); + } + else if (action == HDA_FIXUP_ACT_INIT) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio init\n"); + } + else if (action == HDA_FIXUP_ACT_BUILD) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio build\n"); + } + else if (action == HDA_FIXUP_ACT_FREE) { + myprintk("snd_hda_intel: cs_8409_apple_fixup_gpio free\n"); + } + myprintk("snd_hda_intel: end cs_8409_apple_fixup_gpio\n"); +} + + +// this is from a previous 8409 fixup - remove when see what need to be replaced by + +#ifdef APPLE_FIXUPS + +/* CS8409 */ +enum { + CS8409_MBP131, + CS8409_APPLE_GPIO_0, + CS8409_MBP143, + CS8409_APPLE_GPIO, +}; + +static const struct hda_model_fixup cs8409_apple_models[] = { + { .id = CS8409_MBP131, .name = "mbp131" }, + { .id = CS8409_MBP143, .name = "mbp143" }, + {} +}; + + +static const struct snd_pci_quirk cs8409_apple_fixup_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x3300, "MacBookPro 13,1", CS8409_MBP131), + //SND_PCI_QUIRK(0x106b, 0x3600, "MacBookPro 14,2", CS8409_MBP143), + SND_PCI_QUIRK(0x106b, 0x3900, "MacBookPro 14,3", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x0f00, "Imac 18,2", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x1000, "Imac 18,3", CS8409_MBP143), + //SND_PCI_QUIRK(0x106b, 0x1000, "Imac 19,1", CS8409_MBP143), + {} /* terminator */ +}; + +static const struct hda_pintbl mbp131_pincfgs[] = { + {} /* terminator */ +}; + +static const struct hda_pintbl mbp143_pincfgs[] = { + {} /* terminator */ +}; + +static const struct hda_fixup cs8409_apple_fixups[] = { + [CS8409_MBP131] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp131_pincfgs, + .chained = true, + .chain_id = CS8409_APPLE_GPIO_0, + }, + [CS8409_APPLE_GPIO_0] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs_8409_apple_fixup_gpio, + }, + [CS8409_MBP143] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp143_pincfgs, + .chained = true, + .chain_id = CS8409_APPLE_GPIO, + }, + [CS8409_APPLE_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs_8409_apple_fixup_gpio, + }, +}; +#endif + + +// as per Apple we need to fix the pin config for the linein nid (it defaults as no conn, Line Out which messes the auto config) +// (assuming this swaps as nid swaps for macbook pro/imac) +// except define device as line in not mic in as per Apple, remove the 0x00000100 which is apparently AC_DEFCFG_MISC_NO_PRESENCE +// plus make it a jack like the other jack inputs (so 0x00800001 not 0x90a00101 as per Apple) + +static const struct hda_pintbl macbook_pro_pincfgs[] = { + { 0x45, 0x00800101 }, + { } +}; +static const struct hda_pintbl imac_pincfgs[] = { + { 0x44, 0x00800101 }, + { } +}; + + +static void cs_8409_cs42l83_unsolicited_response(struct hda_codec *codec, unsigned int res); + +static void cs_8409_cs42l83_callback(struct hda_codec *codec, struct hda_jack_callback *event) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_cs42l83_callback %pF\n", cs_8409_cs42l83_callback); + + // for kernel version 4 we stored the unsol res data in private_data + // - now its a separate entity + ////cs_8409_cs42l83_unsol_event_handler(codec, event->private_data); + + cs_8409_cs42l83_unsol_event_handler(codec, event->unsol_res); + + mycodec_info(codec, "cs_8409_cs42l83_callback end\n"); +} + + +static void cs_8409_cs42l83_unsol_event_handler(struct hda_codec *codec, unsigned int unsol_res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler\n"); + + + // print the stored unsol res which seems to be the GPIO pins state + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler - event unsol_res 0x%08x\n",unsol_res); + + cs_8409_cs42l83_unsolicited_response(codec, unsol_res); + + + // now think timers not the way to go + // patch_realtek.c has to deal with similar issues of plugin, headset detection + // and just uses msleep calls + //mod_timer(&cs_8409_hp_timer, jiffies + msecs_to_jiffies(250)); + + // the delayed_work feature might be a way to go tho + + mycodec_info(codec, "cs_8409_cs42l83_unsol_event_handler end\n"); +} + + +// we have 4 automute hooks +// void (*automute_hook)(struct hda_codec *codec); +// void (*hp_automute_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); +// void (*line_automute_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); +// void (*mic_autoswitch_hook)(struct hda_codec *codec, struct hda_jack_callback *cb); + +static void cs_8409_automute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + dev_info(hda_codec_dev(codec), "cs_8409_automute called\n"); +} + +static int cs_8409_boot_setup(struct hda_codec *codec); + + +static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + +static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + +// new attempt if we decide to use the existing fixup mechanism to handle setup + +#ifdef APPLE_FIXUPS +static int patch_cs8409_apple_nouse(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; + int err; + int itm; + + snd_hda_pick_fixup(codec, cs8409_apple_models, cs8409_apple_fixup_tbl, cs8409_apple_fixups); + + mycodec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, + codec->bus->pci->subsystem_vendor, + codec->bus->pci->subsystem_device); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + err = cs8409_apple_parse_auto_config(codec); + if (err < 0) { + cs8409_free(codec); + return err; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; +} +#endif + + +// for Apple we need multiple versions because so far macbook pro and imacs use different nids +static int cs8409_cs42l83_macbook_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ +#ifdef APPLE_PINSENSE_FIXUP + switch (nid) { + case CS8409_CS42L83_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_HP_MIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + case CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static int cs8409_cs42l83_imac_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ +#ifdef APPLE_PINSENSE_FIXUP + switch (nid) { + case CS8409_CS42L83_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_HP_MIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + case CS8409_CS42L83_IMAC_LINEIN_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + default: + break; + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static int cs8409_cs42l83_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, + unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs8409_apple_spec *spec = codec->spec; + //struct cs8409_spec *spec = codec->spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + struct sub_codec *cs42l83 = spec->scodecs[CS8409_CODEC0]; +#endif + + unsigned int nid = ((cmd >> 20) & 0x07f); + unsigned int verb = ((cmd >> 8) & 0x0fff); + + // we have confirmed this is being called + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + +#ifdef APPLE_PINSENSE_FIXUP + // or we could have a single function and the following + if (nid == CS8409_CS42L83_HP_PIN_NID) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->hp_jack_in); + //*res = (cs42l83->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } else if (nid == CS8409_CS42L83_HP_MIC_PIN_NID) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->mic_jack_in); + //*res = (cs42l83->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } else if (nid == spec->linein_nid) { + //myprintk("snd_hda_intel: cs 8409 exec verb 0x%04x 0x%x\n", nid, verb); + if (verb == AC_VERB_GET_PIN_SENSE) { + //myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, cs42l83->linein_jack_in); + //*res = (cs42l83->linein_jack_in) ? AC_PINSENSE_PRESENCE : 0; + // initially use my jack_present flag integer + myprintk("snd_hda_intel: cs 8409 exec verb pin sense 0x%04x %d\n", nid, spec->jack_present); + *res = (spec->jack_present) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + } +#endif + + return spec->exec_verb(dev, cmd, flags, res); +} + +static struct cs8409_apple_spec *cs8409_apple_alloc_spec(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + codec->spec = spec; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + spec->codec = codec; +#endif + //spec->vendor_nid = vendor_nid; + + // this allows power control per nid - for Apple ignoring all power control for now + // this removes eg power up/down on jack plugin/unplug + //codec->power_save_node = 1; + // looks like we need an explicit set - 1 maybe default + codec->power_save_node = 0; +#if 0 + mutex_init(&spec->i2c_mux); + INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker); +#endif + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + + +static int patch_cs8409_apple(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec; + int err; + int itm; + int i; + //hda_nid_t *dac_nids_ptr = NULL; + const struct hda_pincfg *pin; + int fixup_found = 0; + + int explicit = 0; + + //struct hda_pcm *info = NULL; + //struct hda_pcm_stream *hinfo = NULL; + + myprintk("snd_hda_intel: Patching for CS8409 Apple - explicit %d\n", explicit); + //mycodec_info(codec, "Patching for CS8409 Apple - %d\n", explicit); + + //dump_stack(); + + spec = cs8409_apple_alloc_spec(codec); + if (!spec) + return -ENOMEM; + + + spec->vendor_nid = CS8409_VENDOR_NID; + + spec->beep_nid = CS8409_BEEP_NID; + + spec->use_data = 0; + + + // so it appears we need to explicitly apply pre probe fixups here + // note that if the pinconfigs lists are empty the pin config fixup + // is effectively ignored + + //myprintk("cs8409 - 1\n"); + //snd_hda_pick_fixup(codec, cs8409_apple_models, cs8409_apple_fixup_tbl, cs8409_apple_fixups); + + //mycodec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, + // codec->bus->pci->subsystem_vendor, + // codec->bus->pci->subsystem_device); + + //myprintk("cs8409 - 2\n"); + //snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + + // check if we have an implementation for the current machine + // probably should update the fixup tables and use those + // for the moment just replicate the ifs being used + // at the moment seem to get some crash if dont have a known subsystem id + fixup_found = 0; + if (codec->core.subsystem_id == 0x106b3900) { + // macbook pro 14,3 + fixup_found = 1; + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + // macbook pro 14,1?13,1, 14,2 + fixup_found = 1; + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // imac 18,3, 19,1, 18,2, 18,1 + fixup_found = 1; + } + + if (!fixup_found) { + dev_err(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + return -ENODEV; + } + + + + // the following coding is the HDA_FIXUP_ACT_PRE_PROBE phase + + + //issue init verbs here + //snd_hda_add_verbs(codec, cs8409_cs42l83_init_verbh); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0) + + // now going with the Dell way of handling PIN_SENSE for jack plug/unplug events so need this + + myprintk("snd_hda_intel: exec verb setup START\n"); + spec->exec_verb = codec->core.exec_verb; + codec->core.exec_verb = cs8409_cs42l83_exec_verb; +/* + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + codec->core.exec_verb = cs8409_cs42l83_imac_exec_verb; + } + else: + { + codec->core.exec_verb = cs8409_cs42l83_macbook_exec_verb; + } + */ + myprintk("snd_hda_intel: exec verb setup DONE\n"); + + spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l83_codec; + spec->num_scodecs = 1; + spec->scodecs[CS8409_CODEC0]->codec = codec; + + //codec->patch_ops = cs8409_cs42l83_patch_ops; + + if (explicit) + { + //codec->patch_ops = cs_8409_apple_patch_ops_explicit; + } + else + codec->patch_ops = cs_8409_apple_patch_ops; + + + // not sure about these + // the suppress_vmaster is likely reasonable as the Apple way has no dynamic volume controls on either the 8409 chip + // or the amp/cs42l83 chips - the volume is set on boot to a fixed level and never changed + spec->gen.suppress_auto_mute = 1; + spec->gen.no_primary_hp = 1; + spec->gen.suppress_vmaster = 1; + + // we probably also want to suppress auto mic + //spec->gen.suppress_auto_mic = 1; + + // dell GPIO pins are : /* GPIO 5 out, 3,4 in */ + // what is true is the cs8409 interrupt GPIO is pin 1 + // aah - the reset GPIO pin is 0x02 + spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio; + spec->gpio_data = 0; + //spec->gpio_mask = 0x03f; + if (codec->core.subsystem_id == 0x106b3900) { + spec->gpio_mask = 0x07; + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + spec->gpio_mask = 0x0f; + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + spec->gpio_mask = 0x0f; + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + // dell init : /* Basic initial sequence for specific hw configuration */ + //snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs); + + + //snd_array_for_each(&codec->init_pins, i, pin) { + // hda_nid_t nid = pin->nid; + // u32 cfg = pin->cfg; + // myprintk("snd_hda_intel: print pincfg init 0x%02x: 0x%02x: 0x%08x\n", i, nid, cfg); + //} + //snd_array_for_each(&codec->driver_pins, i, pin) { + // hda_nid_t nid = pin->nid; + // u32 cfg = pin->cfg; + // myprintk("snd_hda_intel: print pincfg driver 0x%02x: 0x%02x: 0x%08x\n", i, nid, cfg); + //} + + //for (i = 0; i < codec->core.num_nodes; i++) + // myprintk("snd_hda_intel: print wcaps pre 0x%02x 0x%08x\n", i, codec->wcaps[i]); + + + // as per Apple we need to fix the pin config for the linein nid (it defaults as no conn, Line Out which messes the auto config) + // (assuming this swaps as nid swaps for macbook pro/imac) + // except define device as line in not mic in as per Apple plus make it a jack like + // the other jack inputs (so 0x00800101 not 0x90a00101 as per Apple) + + //static const struct hda_pintbl macbook_pro_pincfgs[] = { + // { 0x45, 0x00800101 }, + // { } + //}; + //static const struct hda_pintbl imac_pincfgs[] = { + // { 0x44, 0x00800101 }, + // { } + //}; + + + // cs8409_fix_caps overrides the jack nid to AC_PINCAP_IMP_SENSE and AC_PINCAP_PRES_DETECT parameters + // and adds AC_WCAP_UNSOL_CAP to the wcaps + // - not sure we should do this for AC_WCAP_UNSOL_CAP for Apple + // - but that depends on how we implement features + // currently we re-write snd_hda_jack_detect_enable_callback_mst and remove the AC_VERB_SET_UNSOLICITED_ENABLE + // setup (because we get unsol responses by default) + // but new idea is could fixup the exec_verb replacement and skip any AC_VERB_SET_UNSOLICITED_ENABLE + // commands received + +#if 1 + // we have one jack but 2 nids to setup - one for output and one for input + // the headphone output/input is same for both imac and macbook pros + //cs8409_fix_caps(codec, CS8409_CS42L83_HP_PIN_NID); + //cs8409_fix_caps(codec, CS8409_CS42L83_HP_MIC_PIN_NID); + // version of cs8409_fix_caps with some debug prints + cs8409_fix_caps_debug(codec, CS8409_CS42L83_HP_PIN_NID); + cs8409_fix_caps_debug(codec, CS8409_CS42L83_HP_MIC_PIN_NID); + + // we also need to setup the linein nid (why is there no lineout??) + // (note that the internal mike is permanent - on the other hand we do need to turn it off if headset with mike + // is plugged in???) + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs8409_fix_caps(codec, CS8409_CS42L83_IMAC_LINEIN_PIN_NID); + } else { + cs8409_fix_caps(codec, CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID); + } +#endif + + //for (i = 0; i < codec->core.num_nodes; i++) + // myprintk("snd_hda_intel: print wcaps post 0x%02x 0x%08x\n", i, codec->wcaps[i]); + + + //if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // snd_hda_apply_pincfgs(codec, imac_pincfgs); + //else + // snd_hda_apply_pincfgs(codec, macbook_pro_pincfgs); + + + // note that the auto config doesnt issue any PIN_SENSE commands but it does use the PINCAP data + // check_auto_mute_availability uses the PINCAP to determine if a nid can have a jack + // via is_jack_detectable - however because suppress_automute is set to 1 + // check_auto_mute_availability exits before calling this function + // maybe because check_auto_mic_availability is also called which also calls is_jack_detectable + // and is not suppressed + // so from this we can gather that all auto_mute/automute functionality must be handled explicitly + // (and not from eg snd_hda_gen_hp_automute/snd_hda_gen_line_automute) + // whereas auto_mic functionality is handled by the generic functions eg snd_hda_gen_mic_autoswitch + + + // Analysis of jack reporting so far: + // the default coding for generic jack reporting seems to be as follows: + // the default snd_hda_jack_unsol_event sets jack_dirty to 1 for the jack entry for the nid + // connected to the jack + // calls any callbacks for jack events (as set by snd_hda_jack_detect_enable_callback_mst) + // and then calls snd_hda_jack_report_sync + // snd_hda_jack_report_sync is the critically important function + // this function calls jack_detect_update which uses an AC_VERB_GET_PIN_SENSE verb to detect + // current jack status then unsets jack_dirty to 0 + // then for all jacks the current pin sense state AND button state is reported to the user level + // via snd_jack_report (a function of jack.c in core directory) + // which updates user level with a snd_kctl_jack_report - a function of ctljack.c in core + // which issues a snd_ctl_notify using SNDRV_CTL_EVENT_MASK_VALUE and the previously prepared + // pin sense state AND button state + + + + // tip sense setups done here + + +#else + if (explicit) + { + //codec->patch_ops = cs_8409_apple_patch_ops_explicit; + } + else + codec->patch_ops = cs_8409_apple_patch_ops; +#endif + + // moved to post auto config + + //spec->gen.pcm_playback_hook = cs_8409_playback_pcm_hook; + + //spec->gen.pcm_capture_hook = cs_8409_capture_pcm_hook; + + //spec->gen.automute_hook = cs_8409_automute; + + //timer_setup(&cs_8409_hp_timer, cs_8409_hp_timer_callback, 0); + + + // use this to cause unsolicited responses to be stored + // but not run + spec->block_unsol = 0; + + INIT_LIST_HEAD(&spec->unsol_list); + + for (itm=0; itm<10; itm++) + { spec->unsol_items_prealloc_used[itm] = 0; } + + + // for the moment set initial jack status to not present + // we will detect if have jack plugged in on boot later + spec->jack_present = 0; + + + spec->headset_type = 0; + + spec->have_mike = 0; + + spec->have_buttons = 0; + + spec->playing = 0; + spec->capturing = 0; + + spec->headset_play_format_setup_needed = 1; + spec->headset_capture_format_setup_needed = 1; + + spec->headset_presetup_done = 0; + + + // use this to distinguish which unsolicited phase we are in + // for the moment - we only seem to get a tag of 0x37 and dont see any + // different tags being setup in OSX logs + spec->headset_phase = 0; + + spec->headset_enable = 0; + + + // setup the intmike and linein nids + // these are swapped between macbook pros and imacs + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->intmike_nid = 0x45; + spec->intmike_adc_nid = 0x23; + spec->linein_nid = 0x44; + spec->linein_amp_nid = 0x22; + } + else + { + spec->intmike_nid = 0x44; + spec->intmike_adc_nid = 0x22; + spec->linein_nid = 0x45; + spec->linein_amp_nid = 0x23; + } + + myprintk("snd_hda_intel: intmike_nid is 0x%02x\n", spec->intmike_nid); + myprintk("snd_hda_intel: intmike_acc_nid is 0x%02x\n", spec->intmike_adc_nid); + + + // ASP is Audio Serial Port + // DMIC is Digital Microphone Input + // + // so it appears that while the intmike/linein nid functions are swapped between macbook pro and imacs + // the digital mike paths associated with the nids are fixed - which means the digital mike path + // swaps between macbooks and imac + // ie nid 0x44 -> 0x22 path 0x82 : 0x0001 ie DMIC1 + // ie nid 0x45 -> 0x23 path 0x82 : 0x0002 ie DMIC2 + + // current status 0x82 reg: + // macbook pro and imac use 0x5400 for speaker amps - ASP1 (nid indpendent??) + // macbook pro and imac use 0xa800 for headset amp - ASP2 (nid indpendent??) + // macbook pro use 0x0001 for internal mike? - DMIC1 (nid dependent) + // macbook imac use 0x0002 for internal mike? - DMIC2 (nid dependent) + // macbook pro use 0x0002 for linein? - DMIC2 (nid dependent) + // macbook imac use 0x0001 for linein? - DMIC1 (nid dependent) + + // headphone/headset output path + // nid 0x0a -> 0x2c + + // headset mike path + // nid 0x3c -> 0x1a + + // combining data from cs4208_...inf with new 8409 updates in patch_cirrus for DEL laptops + // it seems reg 0x0009 is associated with the digital mikes + // vendor reg 0x0009 - 0x0023 : DMIC1_MO=10b, DMIC1/2_SR=1 + // - 0x0003 : DMIC1_MO=00b, DMIC1/2_SR=1 + // - 0x0043 : DMIC2_MO=01b, DMIC1/2_SR=1 + // - 0x0083 : DMIC2_MO=10b, DMIC1/2_SR=1 - this is my guess + // note that Apple also sets bit 0x0010 which so far is undocumented as is not consistent with above data + // - 0x0033 : DMIC1_MO=11b, DMIC1/2_SR=1 + // - 0x0093 : DMIC2_MO=10b, DMIC1_MO=01b?, DMIC1/2_SR=1 + + // it appears we need to swap a number of vendor node register fields because of the above path swap + // vendor reg 0x0009 - associated with internal mike from macbook pros + // OSX function setConnectionSelect + // macbook pro init 0x0013 -> 0x0033 ie 0x0020 bit set + // imac init 0x0013 -> 0x0093 ie 0x0080 bit set + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->reg9_intmike_dmic_mo = 0x0080; // DMIC2_MO=10b + } + else + { + spec->reg9_intmike_dmic_mo = 0x0020; // DMIC1_MO=10b + } + + // vendor reg 0x0082 - this is a very complex reg + // - it appears to contain 2 sort of separate items - the ASP1 and ASP2 enables and the DMIC1/DMIC2 SCL enables + // - 0xfc03 : ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1/2_SCL_EN=1 (was DMIC1_SCL_EN in comments but thinks thats wrong given below) + // - 0xfc01 : (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1) + // - 0xff03 : (ASP1/2_xxx_EN = 1, DMIC1/2_SCL_EN = 1) + // - 0xfd02 : (ASP1/2_xxx_EN = 1, ASP2_MCLK_EN = 0, DMIC2_SCL_EN = 1) + // - 0xfe03 : (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 0, DMIC1/2_SCL_EN = 1) + // - so (ASP1_MCLK_EN is 0x0100 and ASP2_MCLK_EN is 0x0200) + // - from the OSX codes we seem to have + // - 0xa800 : ASP2_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + // - 0x5400 : ASP1_xxx_EN = 1, ASP1/2_MCLK_EN = 0 + + // vendor reg 0x0082 - we need to update the DMIC bit fields but not the ASP bit fields for macbook/imac switch + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + spec->reg82_intmike_dmic_scl = 0x0002; // DMIC2_SCL_EN + spec->reg82_linein_dmic_scl = 0x0001; // DMIC1_SCL_EN + } + else + { + spec->reg82_intmike_dmic_scl = 0x0001; // DMIC1_SCL_EN + spec->reg82_linein_dmic_scl = 0x0002; // DMIC2_SCL_EN + } + + + // the jack detect enable callback can be either in PRE_PROBE or PROBE phase + + myprintk("snd_hda_intel: cs 8409 jack used %d\n",codec->jacktbl.used); + + // we need to figure out how to setup the jack detect callback + // not clear what nid should be used - 0x01 or 0x47 + // added a tag argument because we seem to get a tag + // so far the tag seems to be 0x37 + //cs_8409_hda_jack_detect_enable_callback(codec, 0x01, 0, 0x37, cs_8409_cs42l83_callback); + + //myprintk("snd_hda_intel: cs 8409 jack used callback %d\n",codec->jacktbl.used); + + + // cs8409_pinmux_init(codec); + + + // so it appears we dont get interrupts in the auto config stage + + if (!explicit) + { + + myprintk("snd_hda_intel: pre cs_8409_apple_parse_auto_config\n"); + + err = cs_8409_apple_parse_auto_config(codec); + if (err < 0) + goto error; + + myprintk("snd_hda_intel: post cs_8409_apple_parse_auto_config\n"); + + cs_8409_dump_auto_config(codec, "post cs_8409_apple_parse_auto_config"); + } + + + // dump the rates/format of the afg node + // still havent figured out how the user space gets the allowed formats + // ah - may have figured this + // except that at this point this is NULL - we need to be after build pcms + //info = spec->gen.pcm_rec[0]; + //if (info != NULL) + //{ + // hinfo = &(info->stream[SNDRV_PCM_STREAM_PLAYBACK]); + // if (hinfo != NULL) + // mycodec_dbg(codec, "playback info stream nid 0x%02x rates 0x%08x formats 0x%016llx\n",hinfo->nid,hinfo->rates,hinfo->formats); + // else + // mycodec_dbg(codec, "playback info stream NULL\n"); + //} + //else + // mycodec_dbg(codec, "playback info NULL\n"); + + + // the following code would be the HDA_FIXUP_ACT_PROBE phase + + + // new idea to try and use the gated/gating jack function as we have only one jack + // and this seems to be what gating/gated jacks seem to be designed for + + + + // this is how to fix the format for the streams + // dell : /* Fix Sample Rate to 48kHz */ + //spec->gen.stream_analog_playback = &cs42l83_apple_pcm_analog_playback; + //spec->gen.stream_analog_capture = &cs42l83_apple_pcm_analog_capture; + + + spec->gen.pcm_playback_hook = cs_8409_playback_pcm_hook; + + spec->gen.pcm_capture_hook = cs_8409_capture_pcm_hook; + + spec->gen.automute_hook = cs_8409_automute; + + + // so this seems to be how we setup volume controls for the headphones + // - but this sets up controls on the 8409 to directly update the cs42l42 + // - this is not how apple does things - all volume controls must occur + // prior to this driver + // the problem is this means we need some pulse/alsa configuration to setup + // such controls + + // del : /* Set initial DMIC volume to -26 dB */ + //snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID, + // HDA_INPUT, 0, 0xff, 0x19); + //snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume", + // &cs42l42_dac_volume_mixer); + //snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", + // &cs42l42_adc_volume_mixer); + + + // del : /* Disable Unsolicited Response during boot */ + //cs8409_enable_ur(codec, 0); + + + snd_hda_codec_set_name(codec, "CS8409/CS42L83"); + + + myprintk("snd_hda_intel: cs 8409 jack used post %d\n",codec->jacktbl.used); + +#ifdef MYSOUNDDEBUGFULL + cs_8409_dump_callback(codec); +#else +#ifdef MYSOUNDDEBUG + cs_8409_dump_callback(codec); +#endif +#endif + + + + err = cs_8409_boot_setup(codec); + if (err < 0) + goto error; + + // update the headset phase + spec->headset_phase = 2; + + spec->play_init = 0; + spec->capture_init = 0; + + // init the last play time +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + ktime_get_real_ts64(&(spec->last_play_time)); +#else + getnstimeofday(&(spec->last_play_time)); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + ktime_get_real_ts64(&(spec->first_play_time)); +#else + getnstimeofday(&(spec->first_play_time)); +#endif + + myprintk("snd_hda_intel: Post Patching for CS8409 Apple\n"); + //mycodec_info(codec, "Post Patching for CS8409 Apple\n"); + + return 0; + + error: + cs_8409_apple_free(codec); + return err; +} + + +// for the moment split the new code into an include file + +#include "patch_cirrus_new84.h" + + +// new function to use "vendor" defined commands to run +// a specific code +// has to be here to use functions defined in patch_cirrus_new84.h + +static unsigned int +cs_8409_extended_codec_verb(struct hda_codec *codec, hda_nid_t nid, + int flags, + unsigned int verb, unsigned int parm) +{ + //static inline unsigned int cs_8409_vendor_i2cRead(struct hda_codec *codec, unsigned int i2c_address, + // unsigned int i2c_reg, unsigned int paged) + unsigned int retval1 = 0; + unsigned int retval2 = 0; + unsigned int retval3 = 0; + unsigned int retval4 = 0; + unsigned int retval = 0; + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb nid 0x%02x flags 0x%x verb 0x%03x parm 0x%04x\n", nid, flags, verb, parm); + + if ((verb & 0x0ff8) == 0xf78) + { + retval1 = cs_8409_vendor_i2cWrite(codec, 0x64, 0x2d, parm, 0); + retval2 = cs_8409_vendor_i2cWrite(codec, 0x62, 0x2d, parm, 0); + retval3 = cs_8409_vendor_i2cWrite(codec, 0x74, 0x2d, parm, 0); + retval4 = cs_8409_vendor_i2cWrite(codec, 0x72, 0x2d, parm, 0); + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 1 0x%x\n",retval1); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 2 0x%x\n",retval2); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 3 0x%x\n",retval3); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb wr ret 4 0x%x\n",retval4); + } + else if ((verb & 0x0ff8) == 0xff8) + { + retval1 = cs_8409_vendor_i2cRead(codec, 0x64, 0x2d, 0); + retval2 = cs_8409_vendor_i2cRead(codec, 0x62, 0x2d, 0); + retval3 = cs_8409_vendor_i2cRead(codec, 0x74, 0x2d, 0); + retval4 = cs_8409_vendor_i2cRead(codec, 0x72, 0x2d, 0); + + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 1 0x%x\n",retval1); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 2 0x%x\n",retval2); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 3 0x%x\n",retval3); + myprintk("snd_hda_intel: cs_8409_extended_codec_verb rd ret 4 0x%x\n",retval4); + } + + + retval = retval1; + + return retval; +} + +#ifdef ADD_EXTENDED_VERB +static void cs_8409_set_extended_codec_verb(void) +{ + snd_hda_set_extended_codec_verb(cs_8409_extended_codec_verb); +} +#endif + + diff --git a/patch_cirrus_boot84.h b/patch_cirrus_boot84.h new file mode 100644 index 0000000..4bb1a36 --- /dev/null +++ b/patch_cirrus_boot84.h @@ -0,0 +1,2452 @@ + +static void setup_reset_and_clear(struct hda_codec *codec) +{ + + // so now really dont know why I skipped all this - maybe because they + // almost entirely reads?? + // - except it does clear all the pin configs + // plus does a DBL init + + int retval; + + + mycodec_info(codec, "command nid start setup_node_reset_and_clear\n"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 2); // 0x000f0002 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 3); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 4); // 0x000f0002 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00010001, 5); // 0x000f0004 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00020046, 6); // 0x001f0004 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000101, 7); // 0x001f0005 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_SUBSYSTEM_ID, 0x00000000, 0x106b3900, 8); // 0x001f2000 + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 9); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 10); // 0x000f0002 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000f, 0xe0000019, 11); // 0x001f000f + + + // AppleHDACodecGeneric::start(IOService*) + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_DBL_CODEC_RESET, 0x00000000); // 0x001fff00 + snd_hda_double_reset(codec); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 13); // 0x000f0000 + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000002, 0x00100100, 14); // 0x000f0002 + + + // AppleHDANode::initForNodeID(unsigned short, OSObject*, OSObject*) + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000101, 15); // 0x001f0005 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000f, 0xe0000019, 16); // 0x001f000f + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001a017f, 17); // 0x001f000a +// snd_hda: pcm params 1 bits: 16bit 24bit 32bit rates: 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 96kHz + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 18); // 0x001f000b +// snd_hda: stream format params 1 pcm + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 19); // 0x001f0012 +// snd_hda: amp capabilities 1 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 20); // 0x001f000d +// snd_hda: amp capabilities 1 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + + + // AppleHDAFunctionGroup::initForNodeID?? + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_SUBSYSTEM_ID, 0x00000000, 0x106b3900, 22); // 0x001f2000 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000008, 0x00010000, 23); // 0x001f0008 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0x00000000, 0x00000000, 24); // 0x001f1700 +// snd_hda: gpio direction 1 0x00 in in in in in in in in + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000011, 0xc0000008, 25); // 0x001f0011 +// snd_hda: gpio params 1 [('GPIO', 8), ('GPIO_WAKE', 1), ('GPO', 0), ('GPI', 0), ('GPIO_UNSOL', 1)] + + mycodec_info(codec, "command nid start setup_node_reset_and_clear end\n"); + +} + +static void init_read_all_nodes(struct hda_codec *codec) +{ + int retval; + + + // get node count + // note that there are 4 so called VirtualWidgets which are indexed after the 0x46 count from + // the vendor node at 0x47 + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_PARAMETERS, 0x00000004, 0x00020046, 26); // 0x001f0004 + + + // so apple reads parameters from all nodes + // but setting pin nodes SET_PIN_WIDGET_CONTROL to 0 + + // this appears to be a loop of calls to AppleHDAWidgetCS8409::initForNodeID for each node + // this calls AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) for each node + // and specifically deals with the BEEP node + + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 27); // 0x002f0005 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 28); // 0x002f0009 +// snd_hda: 2 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 29); // 0x002f000f + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 30); // 0x002f000a +// snd_hda: pcm params 2 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 31); // 0x002f000b +// snd_hda: stream format params 2 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 32); // 0x002f0012 +// snd_hda: amp capabilities 2 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 33); // 0x002f000d +// snd_hda: amp capabilities 2 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 34); // 0x002f0009 +// snd_hda: 2 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 36); // 0x003f0005 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 37); // 0x003f0009 +// snd_hda: 3 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 38); // 0x003f000f + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 39); // 0x003f000a +// snd_hda: pcm params 3 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 40); // 0x003f000b +// snd_hda: stream format params 3 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 41); // 0x003f0012 +// snd_hda: amp capabilities 3 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 42); // 0x003f000d +// snd_hda: amp capabilities 3 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 43); // 0x003f0009 +// snd_hda: 3 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 45); // 0x004f0005 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 46); // 0x004f0009 +// snd_hda: 4 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 47); // 0x004f000f + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 48); // 0x004f000a +// snd_hda: pcm params 4 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 49); // 0x004f000b +// snd_hda: stream format params 4 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 50); // 0x004f0012 +// snd_hda: amp capabilities 4 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 51); // 0x004f000d +// snd_hda: amp capabilities 4 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x04, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 52); // 0x004f0009 +// snd_hda: 4 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 54); // 0x005f0005 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 55); // 0x005f0009 +// snd_hda: 5 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 56); // 0x005f000f + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 57); // 0x005f000a +// snd_hda: pcm params 5 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 58); // 0x005f000b +// snd_hda: stream format params 5 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 59); // 0x005f0012 +// snd_hda: amp capabilities 5 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 60); // 0x005f000d +// snd_hda: amp capabilities 5 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x05, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 61); // 0x005f0009 +// snd_hda: 5 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 63); // 0x006f0005 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 64); // 0x006f0009 +// snd_hda: 6 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 65); // 0x006f000f + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 66); // 0x006f000a +// snd_hda: pcm params 6 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 67); // 0x006f000b +// snd_hda: stream format params 6 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 68); // 0x006f0012 +// snd_hda: amp capabilities 6 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 69); // 0x006f000d +// snd_hda: amp capabilities 6 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x06, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 70); // 0x006f0009 +// snd_hda: 6 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 72); // 0x007f0005 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 73); // 0x007f0009 +// snd_hda: 7 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 74); // 0x007f000f + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 75); // 0x007f000a +// snd_hda: pcm params 7 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 76); // 0x007f000b +// snd_hda: stream format params 7 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 77); // 0x007f0012 +// snd_hda: amp capabilities 7 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 78); // 0x007f000d +// snd_hda: amp capabilities 7 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x07, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 79); // 0x007f0009 +// snd_hda: 7 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 81); // 0x008f0005 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 82); // 0x008f0009 +// snd_hda: 8 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 83); // 0x008f000f + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 84); // 0x008f000a +// snd_hda: pcm params 8 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 85); // 0x008f000b +// snd_hda: stream format params 8 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 86); // 0x008f0012 +// snd_hda: amp capabilities 8 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 87); // 0x008f000d +// snd_hda: amp capabilities 8 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x08, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 88); // 0x008f0009 +// snd_hda: 8 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 90); // 0x009f0005 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 91); // 0x009f0009 +// snd_hda: 9 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 92); // 0x009f000f + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 93); // 0x009f000a +// snd_hda: pcm params 9 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 94); // 0x009f000b +// snd_hda: stream format params 9 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 95); // 0x009f0012 +// snd_hda: amp capabilities 9 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 96); // 0x009f000d +// snd_hda: amp capabilities 9 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x09, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 97); // 0x009f0009 +// snd_hda: 9 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 99); // 0x00af0005 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 100); // 0x00af0009 +// snd_hda: 10 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 101); // 0x00af000f + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 102); // 0x00af000a +// snd_hda: pcm params 10 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 103); // 0x00af000b +// snd_hda: stream format params 10 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 104); // 0x00af0012 +// snd_hda: amp capabilities 10 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 105); // 0x00af000d +// snd_hda: amp capabilities 10 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 106); // 0x00af0009 +// snd_hda: 10 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 108); // 0x00bf0005 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 109); // 0x00bf0009 +// snd_hda: 11 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 110); // 0x00bf000f + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 111); // 0x00bf000a +// snd_hda: pcm params 11 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 112); // 0x00bf000b +// snd_hda: stream format params 11 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 113); // 0x00bf0012 +// snd_hda: amp capabilities 11 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 114); // 0x00bf000d +// snd_hda: amp capabilities 11 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 115); // 0x00bf0009 +// snd_hda: 11 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 117); // 0x00cf0005 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 118); // 0x00cf0009 +// snd_hda: 12 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 119); // 0x00cf000f + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 120); // 0x00cf000a +// snd_hda: pcm params 12 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 121); // 0x00cf000b +// snd_hda: stream format params 12 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 122); // 0x00cf0012 +// snd_hda: amp capabilities 12 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 123); // 0x00cf000d +// snd_hda: amp capabilities 12 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 124); // 0x00cf0009 +// snd_hda: 12 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 126); // 0x00df0005 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 127); // 0x00df0009 +// snd_hda: 13 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 128); // 0x00df000f + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 129); // 0x00df000a +// snd_hda: pcm params 13 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 130); // 0x00df000b +// snd_hda: stream format params 13 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 131); // 0x00df0012 +// snd_hda: amp capabilities 13 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 132); // 0x00df000d +// snd_hda: amp capabilities 13 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 133); // 0x00df0009 +// snd_hda: 13 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 135); // 0x00ef0005 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 136); // 0x00ef0009 +// snd_hda: 14 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 137); // 0x00ef000f + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 138); // 0x00ef000a +// snd_hda: pcm params 14 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 139); // 0x00ef000b +// snd_hda: stream format params 14 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 140); // 0x00ef0012 +// snd_hda: amp capabilities 14 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 141); // 0x00ef000d +// snd_hda: amp capabilities 14 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 142); // 0x00ef0009 +// snd_hda: 14 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 144); // 0x00ff0005 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 145); // 0x00ff0009 +// snd_hda: 15 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 146); // 0x00ff000f + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 147); // 0x00ff000a +// snd_hda: pcm params 15 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 148); // 0x00ff000b +// snd_hda: stream format params 15 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 149); // 0x00ff0012 +// snd_hda: amp capabilities 15 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 150); // 0x00ff000d +// snd_hda: amp capabilities 15 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x0f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 151); // 0x00ff0009 +// snd_hda: 15 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 153); // 0x010f0005 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 154); // 0x010f0009 +// snd_hda: 16 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 155); // 0x010f000f + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 156); // 0x010f000a +// snd_hda: pcm params 16 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 157); // 0x010f000b +// snd_hda: stream format params 16 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 158); // 0x010f0012 +// snd_hda: amp capabilities 16 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 159); // 0x010f000d +// snd_hda: amp capabilities 16 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x10, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 160); // 0x010f0009 +// snd_hda: 16 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 162); // 0x011f0005 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 163); // 0x011f0009 +// snd_hda: 17 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 164); // 0x011f000f + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 165); // 0x011f000a +// snd_hda: pcm params 17 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 166); // 0x011f000b +// snd_hda: stream format params 17 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 167); // 0x011f0012 +// snd_hda: amp capabilities 17 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 168); // 0x011f000d +// snd_hda: amp capabilities 17 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x11, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00060001, 169); // 0x011f0009 +// snd_hda: 17 AC_WID_AUD_OUT ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 6 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 171); // 0x012f0005 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 172); // 0x012f0009 +// snd_hda: 18 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 173); // 0x012f000f + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 174); // 0x012f000a +// snd_hda: pcm params 18 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 175); // 0x012f000b +// snd_hda: stream format params 18 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 176); // 0x012f0012 +// snd_hda: amp capabilities 18 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 177); // 0x012f000d +// snd_hda: amp capabilities 18 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 178); // 0x012f0009 +// snd_hda: 18 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 179); // 0x012f000e + retval = snd_hda_codec_read_check(codec, 0x12, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000034, 180); // 0x012f0200 +// snd_hda: connection list 18 <- 52 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 182); // 0x013f0005 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 183); // 0x013f0009 +// snd_hda: 19 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 184); // 0x013f000f + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 185); // 0x013f000a +// snd_hda: pcm params 19 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 186); // 0x013f000b +// snd_hda: stream format params 19 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 187); // 0x013f0012 +// snd_hda: amp capabilities 19 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 188); // 0x013f000d +// snd_hda: amp capabilities 19 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 189); // 0x013f0009 +// snd_hda: 19 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 190); // 0x013f000e + retval = snd_hda_codec_read_check(codec, 0x13, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000035, 191); // 0x013f0200 +// snd_hda: connection list 19 <- 53 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 193); // 0x014f0005 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 194); // 0x014f0009 +// snd_hda: 20 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 195); // 0x014f000f + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 196); // 0x014f000a +// snd_hda: pcm params 20 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 197); // 0x014f000b +// snd_hda: stream format params 20 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 198); // 0x014f0012 +// snd_hda: amp capabilities 20 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 199); // 0x014f000d +// snd_hda: amp capabilities 20 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 200); // 0x014f0009 +// snd_hda: 20 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 201); // 0x014f000e + retval = snd_hda_codec_read_check(codec, 0x14, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000036, 202); // 0x014f0200 +// snd_hda: connection list 20 <- 54 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 204); // 0x015f0005 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 205); // 0x015f0009 +// snd_hda: 21 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 206); // 0x015f000f + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 207); // 0x015f000a +// snd_hda: pcm params 21 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 208); // 0x015f000b +// snd_hda: stream format params 21 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 209); // 0x015f0012 +// snd_hda: amp capabilities 21 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 210); // 0x015f000d +// snd_hda: amp capabilities 21 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 211); // 0x015f0009 +// snd_hda: 21 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 212); // 0x015f000e + retval = snd_hda_codec_read_check(codec, 0x15, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000037, 213); // 0x015f0200 +// snd_hda: connection list 21 <- 55 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 215); // 0x016f0005 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 216); // 0x016f0009 +// snd_hda: 22 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 217); // 0x016f000f + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 218); // 0x016f000a +// snd_hda: pcm params 22 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 219); // 0x016f000b +// snd_hda: stream format params 22 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 220); // 0x016f0012 +// snd_hda: amp capabilities 22 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 221); // 0x016f000d +// snd_hda: amp capabilities 22 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 222); // 0x016f0009 +// snd_hda: 22 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 223); // 0x016f000e + retval = snd_hda_codec_read_check(codec, 0x16, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000038, 224); // 0x016f0200 +// snd_hda: connection list 22 <- 56 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 226); // 0x017f0005 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 227); // 0x017f0009 +// snd_hda: 23 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 228); // 0x017f000f + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 229); // 0x017f000a +// snd_hda: pcm params 23 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 230); // 0x017f000b +// snd_hda: stream format params 23 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 231); // 0x017f0012 +// snd_hda: amp capabilities 23 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 232); // 0x017f000d +// snd_hda: amp capabilities 23 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 233); // 0x017f0009 +// snd_hda: 23 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 234); // 0x017f000e + retval = snd_hda_codec_read_check(codec, 0x17, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000039, 235); // 0x017f0200 +// snd_hda: connection list 23 <- 57 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 237); // 0x018f0005 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 238); // 0x018f0009 +// snd_hda: 24 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 239); // 0x018f000f + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 240); // 0x018f000a +// snd_hda: pcm params 24 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 241); // 0x018f000b +// snd_hda: stream format params 24 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 242); // 0x018f0012 +// snd_hda: amp capabilities 24 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 243); // 0x018f000d +// snd_hda: amp capabilities 24 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 244); // 0x018f0009 +// snd_hda: 24 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 245); // 0x018f000e + retval = snd_hda_codec_read_check(codec, 0x18, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003a, 246); // 0x018f0200 +// snd_hda: connection list 24 <- 58 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 248); // 0x019f0005 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 249); // 0x019f0009 +// snd_hda: 25 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 250); // 0x019f000f + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 251); // 0x019f000a +// snd_hda: pcm params 25 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 252); // 0x019f000b +// snd_hda: stream format params 25 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 253); // 0x019f0012 +// snd_hda: amp capabilities 25 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 254); // 0x019f000d +// snd_hda: amp capabilities 25 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 255); // 0x019f0009 +// snd_hda: 25 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 256); // 0x019f000e + retval = snd_hda_codec_read_check(codec, 0x19, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003b, 257); // 0x019f0200 +// snd_hda: connection list 25 <- 59 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 259); // 0x01af0005 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 260); // 0x01af0009 +// snd_hda: 26 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 261); // 0x01af000f + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 262); // 0x01af000a +// snd_hda: pcm params 26 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 263); // 0x01af000b +// snd_hda: stream format params 26 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 264); // 0x01af0012 +// snd_hda: amp capabilities 26 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 265); // 0x01af000d +// snd_hda: amp capabilities 26 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 266); // 0x01af0009 +// snd_hda: 26 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 267); // 0x01af000e + retval = snd_hda_codec_read_check(codec, 0x1a, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003c, 268); // 0x01af0200 +// snd_hda: connection list 26 <- 60 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 270); // 0x01bf0005 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 271); // 0x01bf0009 +// snd_hda: 27 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 272); // 0x01bf000f + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 273); // 0x01bf000a +// snd_hda: pcm params 27 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 274); // 0x01bf000b +// snd_hda: stream format params 27 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 275); // 0x01bf0012 +// snd_hda: amp capabilities 27 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 276); // 0x01bf000d +// snd_hda: amp capabilities 27 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 277); // 0x01bf0009 +// snd_hda: 27 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 278); // 0x01bf000e + retval = snd_hda_codec_read_check(codec, 0x1b, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003d, 279); // 0x01bf0200 +// snd_hda: connection list 27 <- 61 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 281); // 0x01cf0005 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 282); // 0x01cf0009 +// snd_hda: 28 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 283); // 0x01cf000f + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 284); // 0x01cf000a +// snd_hda: pcm params 28 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 285); // 0x01cf000b +// snd_hda: stream format params 28 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 286); // 0x01cf0012 +// snd_hda: amp capabilities 28 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 287); // 0x01cf000d +// snd_hda: amp capabilities 28 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 288); // 0x01cf0009 +// snd_hda: 28 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 289); // 0x01cf000e + retval = snd_hda_codec_read_check(codec, 0x1c, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003e, 290); // 0x01cf0200 +// snd_hda: connection list 28 <- 62 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 292); // 0x01df0005 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 293); // 0x01df0009 +// snd_hda: 29 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 294); // 0x01df000f + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 295); // 0x01df000a +// snd_hda: pcm params 29 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 296); // 0x01df000b +// snd_hda: stream format params 29 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 297); // 0x01df0012 +// snd_hda: amp capabilities 29 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 298); // 0x01df000d +// snd_hda: amp capabilities 29 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 299); // 0x01df0009 +// snd_hda: 29 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 300); // 0x01df000e + retval = snd_hda_codec_read_check(codec, 0x1d, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000003f, 301); // 0x01df0200 +// snd_hda: connection list 29 <- 63 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 303); // 0x01ef0005 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 304); // 0x01ef0009 +// snd_hda: 30 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 305); // 0x01ef000f + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 306); // 0x01ef000a +// snd_hda: pcm params 30 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 307); // 0x01ef000b +// snd_hda: stream format params 30 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 308); // 0x01ef0012 +// snd_hda: amp capabilities 30 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 309); // 0x01ef000d +// snd_hda: amp capabilities 30 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 310); // 0x01ef0009 +// snd_hda: 30 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 311); // 0x01ef000e + retval = snd_hda_codec_read_check(codec, 0x1e, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000040, 312); // 0x01ef0200 +// snd_hda: connection list 30 <- 64 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 314); // 0x01ff0005 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 315); // 0x01ff0009 +// snd_hda: 31 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 316); // 0x01ff000f + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 317); // 0x01ff000a +// snd_hda: pcm params 31 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 318); // 0x01ff000b +// snd_hda: stream format params 31 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 319); // 0x01ff0012 +// snd_hda: amp capabilities 31 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 320); // 0x01ff000d +// snd_hda: amp capabilities 31 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 321); // 0x01ff0009 +// snd_hda: 31 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 322); // 0x01ff000e + retval = snd_hda_codec_read_check(codec, 0x1f, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000041, 323); // 0x01ff0200 +// snd_hda: connection list 31 <- 65 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 325); // 0x020f0005 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 326); // 0x020f0009 +// snd_hda: 32 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 327); // 0x020f000f + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 328); // 0x020f000a +// snd_hda: pcm params 32 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 329); // 0x020f000b +// snd_hda: stream format params 32 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 330); // 0x020f0012 +// snd_hda: amp capabilities 32 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 331); // 0x020f000d +// snd_hda: amp capabilities 32 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 332); // 0x020f0009 +// snd_hda: 32 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 333); // 0x020f000e + retval = snd_hda_codec_read_check(codec, 0x20, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000042, 334); // 0x020f0200 +// snd_hda: connection list 32 <- 66 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 336); // 0x021f0005 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 337); // 0x021f0009 +// snd_hda: 33 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 338); // 0x021f000f + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 339); // 0x021f000a +// snd_hda: pcm params 33 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 340); // 0x021f000b +// snd_hda: stream format params 33 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 341); // 0x021f0012 +// snd_hda: amp capabilities 33 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 342); // 0x021f000d +// snd_hda: amp capabilities 33 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00160101, 343); // 0x021f0009 +// snd_hda: 33 AC_WID_AUD_IN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 6 1 + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 344); // 0x021f000e + retval = snd_hda_codec_read_check(codec, 0x21, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000043, 345); // 0x021f0200 +// snd_hda: connection list 33 <- 67 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 347); // 0x022f0005 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 348); // 0x022f0009 +// snd_hda: 34 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 349); // 0x022f000f + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 350); // 0x022f000a +// snd_hda: pcm params 34 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 351); // 0x022f000b +// snd_hda: stream format params 34 pcm + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 352); // 0x022f0012 +// snd_hda: amp capabilities 34 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 353); // 0x022f000d +// snd_hda: amp capabilities 34 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 354); // 0x022f0009 +// snd_hda: 34 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + hda_set_node_power_state(codec, 0x22, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 356); // 0x022f000a +// snd_hda: pcm params 34 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 357); // 0x022f000b +// snd_hda: stream format params 34 pcm + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 358); // 0x022f000d +// snd_hda: amp capabilities 34 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 359); // 0x022f000e + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000044, 360); // 0x022f0200 +// snd_hda: connection list 34 <- 68 + retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 361); // 0x022f000f + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + hda_set_node_power_state(codec, 0x22, AC_PWRST_D3); + + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 364); // 0x023f0005 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 365); // 0x023f0009 +// snd_hda: 35 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 366); // 0x023f000f + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 367); // 0x023f000a +// snd_hda: pcm params 35 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 368); // 0x023f000b +// snd_hda: stream format params 35 pcm + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 369); // 0x023f0012 +// snd_hda: amp capabilities 35 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 370); // 0x023f000d +// snd_hda: amp capabilities 35 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0018051b, 371); // 0x023f0009 +// snd_hda: 35 AC_WID_AUD_IN ['AC_WCAP_STEREO', 'AC_WCAP_CONN_LIST', 'AC_WCAP_POWER', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_FORMAT_OVRD', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 8 1 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + hda_set_node_power_state(codec, 0x23, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x001e01f5, 373); // 0x023f000a +// snd_hda: pcm params 35 bits: 16bit 20bit 24bit 32bit rates: 8kHz 16kHz 32kHz 44.1kHz 48kHz 88.2kHz 96kHz + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000001, 374); // 0x023f000b +// snd_hda: stream format params 35 pcm + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x80033f33, 375); // 0x023f000d +// snd_hda: amp capabilities 35 input 0x80033f33 offset 0x33 numsteps 0x3f stepsize 0x03 mute 1 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 376); // 0x023f000e + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000045, 377); // 0x023f0200 +// snd_hda: connection list 35 <- 69 + retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x80000009, 378); // 0x023f000f + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + hda_set_node_power_state(codec, 0x23, AC_PWRST_D3); + + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 381); // 0x024f0005 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 382); // 0x024f0009 +// snd_hda: 36 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 383); // 0x024f000f + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 384); // 0x024f0012 +// snd_hda: amp capabilities 36 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 385); // 0x024f000d +// snd_hda: amp capabilities 36 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 386); // 0x024f0009 +// snd_hda: 36 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x90100110, 387); // 0x024f1c00 +// snd_hda: pin config default 0x90100110 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 1 conn type 0 color 0 misc 1 def assoc 1 seq 0 +// snd_hda: pin config default 0x90100110 port conn Fixed loc ext Int loc geom N/A default device Speaker conn type Unknown color Unknown misc Jack Detect Override def assoc 1 seq 0 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 388); // 0x024f000c +// snd_hda: 36 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 389); // 0x024f000e + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000002, 390); // 0x024f0200 +// snd_hda: connection list 36 <- 2 + retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 391); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02470700 +// snd_hda: 36 [] + + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 394); // 0x025f0005 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 395); // 0x025f0009 +// snd_hda: 37 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 396); // 0x025f000f + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 397); // 0x025f0012 +// snd_hda: amp capabilities 37 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 398); // 0x025f000d +// snd_hda: amp capabilities 37 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 399); // 0x025f0009 +// snd_hda: 37 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x90100111, 400); // 0x025f1c00 +// snd_hda: pin config default 0x90100111 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 1 conn type 0 color 0 misc 1 def assoc 1 seq 1 +// snd_hda: pin config default 0x90100111 port conn Fixed loc ext Int loc geom N/A default device Speaker conn type Unknown color Unknown misc Jack Detect Override def assoc 1 seq 1 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 401); // 0x025f000c +// snd_hda: 37 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 402); // 0x025f000e + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000003, 403); // 0x025f0200 +// snd_hda: connection list 37 <- 3 + retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 404); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02570700 +// snd_hda: 37 [] + + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 407); // 0x026f0005 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 408); // 0x026f0009 +// snd_hda: 38 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 409); // 0x026f000f + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 410); // 0x026f0012 +// snd_hda: amp capabilities 38 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 411); // 0x026f000d +// snd_hda: amp capabilities 38 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 412); // 0x026f0009 +// snd_hda: 38 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 413); // 0x026f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 414); // 0x026f000c +// snd_hda: 38 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 415); // 0x026f000e + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000004, 416); // 0x026f0200 +// snd_hda: connection list 38 <- 4 + retval = snd_hda_codec_read_check(codec, 0x26, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 417); // 0x026f0700 + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02670700 +// snd_hda: 38 [] + + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 420); // 0x027f0005 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 421); // 0x027f0009 +// snd_hda: 39 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 422); // 0x027f000f + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 423); // 0x027f0012 +// snd_hda: amp capabilities 39 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 424); // 0x027f000d +// snd_hda: amp capabilities 39 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 425); // 0x027f0009 +// snd_hda: 39 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 426); // 0x027f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 427); // 0x027f000c +// snd_hda: 39 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 428); // 0x027f000e + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000005, 429); // 0x027f0200 +// snd_hda: connection list 39 <- 5 + retval = snd_hda_codec_read_check(codec, 0x27, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 430); // 0x027f0700 + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02770700 +// snd_hda: 39 [] + + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 433); // 0x028f0005 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 434); // 0x028f0009 +// snd_hda: 40 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 435); // 0x028f000f + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 436); // 0x028f0012 +// snd_hda: amp capabilities 40 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 437); // 0x028f000d +// snd_hda: amp capabilities 40 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 438); // 0x028f0009 +// snd_hda: 40 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 439); // 0x028f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 440); // 0x028f000c +// snd_hda: 40 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 441); // 0x028f000e + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000006, 442); // 0x028f0200 +// snd_hda: connection list 40 <- 6 + retval = snd_hda_codec_read_check(codec, 0x28, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 443); // 0x028f0700 + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02870700 +// snd_hda: 40 [] + + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 446); // 0x029f0005 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 447); // 0x029f0009 +// snd_hda: 41 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 448); // 0x029f000f + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 449); // 0x029f0012 +// snd_hda: amp capabilities 41 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 450); // 0x029f000d +// snd_hda: amp capabilities 41 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 451); // 0x029f0009 +// snd_hda: 41 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 452); // 0x029f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 453); // 0x029f000c +// snd_hda: 41 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 454); // 0x029f000e + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000007, 455); // 0x029f0200 +// snd_hda: connection list 41 <- 7 + retval = snd_hda_codec_read_check(codec, 0x29, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 456); // 0x029f0700 + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02970700 +// snd_hda: 41 [] + + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 459); // 0x02af0005 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 460); // 0x02af0009 +// snd_hda: 42 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 461); // 0x02af000f + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 462); // 0x02af0012 +// snd_hda: amp capabilities 42 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 463); // 0x02af000d +// snd_hda: amp capabilities 42 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 464); // 0x02af0009 +// snd_hda: 42 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 465); // 0x02af1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 466); // 0x02af000c +// snd_hda: 42 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 467); // 0x02af000e + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000008, 468); // 0x02af0200 +// snd_hda: connection list 42 <- 8 + retval = snd_hda_codec_read_check(codec, 0x2a, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 469); // 0x02af0700 + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02a70700 +// snd_hda: 42 [] + + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 472); // 0x02bf0005 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 473); // 0x02bf0009 +// snd_hda: 43 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 474); // 0x02bf000f + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 475); // 0x02bf0012 +// snd_hda: amp capabilities 43 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 476); // 0x02bf000d +// snd_hda: amp capabilities 43 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 477); // 0x02bf0009 +// snd_hda: 43 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 478); // 0x02bf1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 479); // 0x02bf000c +// snd_hda: 43 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 480); // 0x02bf000e + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000009, 481); // 0x02bf0200 +// snd_hda: connection list 43 <- 9 + retval = snd_hda_codec_read_check(codec, 0x2b, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 482); // 0x02bf0700 + snd_hda_codec_write(codec, 0x2b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02b70700 +// snd_hda: 43 [] + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 485); // 0x02cf0005 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 486); // 0x02cf0009 +// snd_hda: 44 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 487); // 0x02cf000f + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 488); // 0x02cf0012 +// snd_hda: amp capabilities 44 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 489); // 0x02cf000d +// snd_hda: amp capabilities 44 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 490); // 0x02cf0009 +// snd_hda: 44 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x002b4020, 491); // 0x02cf1c00 +// snd_hda: pin config default 0x002b4020 port conn 0 location 0x0 loc ext 0 loc geom 0 default device 2 conn type 11 color 4 misc 0 def assoc 2 seq 0 +// snd_hda: pin config default 0x002b4020 port conn Jack loc ext Ext loc geom N/A default device HP Out conn type Combination color Green misc undef def assoc 2 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 492); // 0x02cf000c +// snd_hda: 44 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 493); // 0x02cf000e + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000a, 494); // 0x02cf0200 +// snd_hda: connection list 44 <- 10 + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 495); // 0x02cf0700 + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02c70700 +// snd_hda: 44 [] + + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 498); // 0x02df0005 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 499); // 0x02df0009 +// snd_hda: 45 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 500); // 0x02df000f + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 501); // 0x02df0012 +// snd_hda: amp capabilities 45 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 502); // 0x02df000d +// snd_hda: amp capabilities 45 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 503); // 0x02df0009 +// snd_hda: 45 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 504); // 0x02df1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 505); // 0x02df000c +// snd_hda: 45 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 506); // 0x02df000e + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000b, 507); // 0x02df0200 +// snd_hda: connection list 45 <- 11 + retval = snd_hda_codec_read_check(codec, 0x2d, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 508); // 0x02df0700 + snd_hda_codec_write(codec, 0x2d, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02d70700 +// snd_hda: 45 [] + + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 511); // 0x02ef0005 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 512); // 0x02ef0009 +// snd_hda: 46 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 513); // 0x02ef000f + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 514); // 0x02ef0012 +// snd_hda: amp capabilities 46 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 515); // 0x02ef000d +// snd_hda: amp capabilities 46 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 516); // 0x02ef0009 +// snd_hda: 46 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 517); // 0x02ef1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 518); // 0x02ef000c +// snd_hda: 46 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 519); // 0x02ef000e + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000c, 520); // 0x02ef0200 +// snd_hda: connection list 46 <- 12 + retval = snd_hda_codec_read_check(codec, 0x2e, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 521); // 0x02ef0700 + snd_hda_codec_write(codec, 0x2e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02e70700 +// snd_hda: 46 [] + + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 524); // 0x02ff0005 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 525); // 0x02ff0009 +// snd_hda: 47 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 526); // 0x02ff000f + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 527); // 0x02ff0012 +// snd_hda: amp capabilities 47 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 528); // 0x02ff000d +// snd_hda: amp capabilities 47 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 529); // 0x02ff0009 +// snd_hda: 47 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 530); // 0x02ff1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 531); // 0x02ff000c +// snd_hda: 47 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 532); // 0x02ff000e + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000d, 533); // 0x02ff0200 +// snd_hda: connection list 47 <- 13 + retval = snd_hda_codec_read_check(codec, 0x2f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 534); // 0x02ff0700 + snd_hda_codec_write(codec, 0x2f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02f70700 +// snd_hda: 47 [] + + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 537); // 0x030f0005 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 538); // 0x030f0009 +// snd_hda: 48 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 539); // 0x030f000f + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 540); // 0x030f0012 +// snd_hda: amp capabilities 48 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 541); // 0x030f000d +// snd_hda: amp capabilities 48 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 542); // 0x030f0009 +// snd_hda: 48 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 543); // 0x030f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 544); // 0x030f000c +// snd_hda: 48 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 545); // 0x030f000e + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000e, 546); // 0x030f0200 +// snd_hda: connection list 48 <- 14 + retval = snd_hda_codec_read_check(codec, 0x30, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 547); // 0x030f0700 + snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03070700 +// snd_hda: 48 [] + + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 550); // 0x031f0005 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 551); // 0x031f0009 +// snd_hda: 49 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 552); // 0x031f000f + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 553); // 0x031f0012 +// snd_hda: amp capabilities 49 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 554); // 0x031f000d +// snd_hda: amp capabilities 49 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 555); // 0x031f0009 +// snd_hda: 49 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 556); // 0x031f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 557); // 0x031f000c +// snd_hda: 49 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 558); // 0x031f000e + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x0000000f, 559); // 0x031f0200 +// snd_hda: connection list 49 <- 15 + retval = snd_hda_codec_read_check(codec, 0x31, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 560); // 0x031f0700 + snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03170700 +// snd_hda: 49 [] + + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 563); // 0x032f0005 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 564); // 0x032f0009 +// snd_hda: 50 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 565); // 0x032f000f + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 566); // 0x032f0012 +// snd_hda: amp capabilities 50 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 567); // 0x032f000d +// snd_hda: amp capabilities 50 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 568); // 0x032f0009 +// snd_hda: 50 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 569); // 0x032f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 570); // 0x032f000c +// snd_hda: 50 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 571); // 0x032f000e + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000010, 572); // 0x032f0200 +// snd_hda: connection list 50 <- 16 + retval = snd_hda_codec_read_check(codec, 0x32, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 573); // 0x032f0700 + snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03270700 +// snd_hda: 50 [] + + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 576); // 0x033f0005 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 577); // 0x033f0009 +// snd_hda: 51 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 578); // 0x033f000f + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 579); // 0x033f0012 +// snd_hda: amp capabilities 51 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 580); // 0x033f000d +// snd_hda: amp capabilities 51 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400101, 581); // 0x033f0009 +// snd_hda: 51 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_CONN_LIST'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 582); // 0x033f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000010, 583); // 0x033f000c +// snd_hda: 51 ['AC_PINCAP_OUT'] [] + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_PARAMETERS, 0x0000000e, 0x00000001, 584); // 0x033f000e + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_CONNECT_LIST, 0x00000000, 0x00000011, 585); // 0x033f0200 +// snd_hda: connection list 51 <- 17 + retval = snd_hda_codec_read_check(codec, 0x33, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 586); // 0x033f0700 + snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03370700 +// snd_hda: 51 [] + + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 589); // 0x034f0005 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 590); // 0x034f0009 +// snd_hda: 52 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 591); // 0x034f000f + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 592); // 0x034f0012 +// snd_hda: amp capabilities 52 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 593); // 0x034f000d +// snd_hda: amp capabilities 52 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 594); // 0x034f0009 +// snd_hda: 52 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 595); // 0x034f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 596); // 0x034f000c +// snd_hda: 52 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x34, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 597); // 0x034f0700 + snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03470700 +// snd_hda: 52 [] + + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 600); // 0x035f0005 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 601); // 0x035f0009 +// snd_hda: 53 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 602); // 0x035f000f + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 603); // 0x035f0012 +// snd_hda: amp capabilities 53 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 604); // 0x035f000d +// snd_hda: amp capabilities 53 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 605); // 0x035f0009 +// snd_hda: 53 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 606); // 0x035f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 607); // 0x035f000c +// snd_hda: 53 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x35, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 608); // 0x035f0700 + snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03570700 +// snd_hda: 53 [] + + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 611); // 0x036f0005 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 612); // 0x036f0009 +// snd_hda: 54 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 613); // 0x036f000f + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 614); // 0x036f0012 +// snd_hda: amp capabilities 54 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 615); // 0x036f000d +// snd_hda: amp capabilities 54 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 616); // 0x036f0009 +// snd_hda: 54 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 617); // 0x036f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 618); // 0x036f000c +// snd_hda: 54 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x36, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 619); // 0x036f0700 + snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03670700 +// snd_hda: 54 [] + + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 622); // 0x037f0005 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 623); // 0x037f0009 +// snd_hda: 55 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 624); // 0x037f000f + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 625); // 0x037f0012 +// snd_hda: amp capabilities 55 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 626); // 0x037f000d +// snd_hda: amp capabilities 55 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 627); // 0x037f0009 +// snd_hda: 55 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 628); // 0x037f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 629); // 0x037f000c +// snd_hda: 55 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x37, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 630); // 0x037f0700 + snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03770700 +// snd_hda: 55 [] + + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 633); // 0x038f0005 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 634); // 0x038f0009 +// snd_hda: 56 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 635); // 0x038f000f + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 636); // 0x038f0012 +// snd_hda: amp capabilities 56 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 637); // 0x038f000d +// snd_hda: amp capabilities 56 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 638); // 0x038f0009 +// snd_hda: 56 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 639); // 0x038f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 640); // 0x038f000c +// snd_hda: 56 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x38, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 641); // 0x038f0700 + snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03870700 +// snd_hda: 56 [] + + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 644); // 0x039f0005 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 645); // 0x039f0009 +// snd_hda: 57 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 646); // 0x039f000f + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 647); // 0x039f0012 +// snd_hda: amp capabilities 57 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 648); // 0x039f000d +// snd_hda: amp capabilities 57 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 649); // 0x039f0009 +// snd_hda: 57 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 650); // 0x039f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 651); // 0x039f000c +// snd_hda: 57 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x39, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 652); // 0x039f0700 + snd_hda_codec_write(codec, 0x39, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03970700 +// snd_hda: 57 [] + + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 655); // 0x03af0005 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 656); // 0x03af0009 +// snd_hda: 58 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 657); // 0x03af000f + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 658); // 0x03af0012 +// snd_hda: amp capabilities 58 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 659); // 0x03af000d +// snd_hda: amp capabilities 58 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 660); // 0x03af0009 +// snd_hda: 58 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 661); // 0x03af1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 662); // 0x03af000c +// snd_hda: 58 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3a, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 663); // 0x03af0700 + snd_hda_codec_write(codec, 0x3a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03a70700 +// snd_hda: 58 [] + + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 666); // 0x03bf0005 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 667); // 0x03bf0009 +// snd_hda: 59 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 668); // 0x03bf000f + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 669); // 0x03bf0012 +// snd_hda: amp capabilities 59 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 670); // 0x03bf000d +// snd_hda: amp capabilities 59 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 671); // 0x03bf0009 +// snd_hda: 59 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 672); // 0x03bf1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 673); // 0x03bf000c +// snd_hda: 59 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3b, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 674); // 0x03bf0700 + snd_hda_codec_write(codec, 0x3b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03b70700 +// snd_hda: 59 [] + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 677); // 0x03cf0005 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 678); // 0x03cf0009 +// snd_hda: 60 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 679); // 0x03cf000f + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 680); // 0x03cf0012 +// snd_hda: amp capabilities 60 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 681); // 0x03cf000d +// snd_hda: amp capabilities 60 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 682); // 0x03cf0009 +// snd_hda: 60 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x00ab9030, 683); // 0x03cf1c00 +// snd_hda: pin config default 0x00ab9030 port conn 0 location 0x0 loc ext 0 loc geom 0 default device 10 conn type 11 color 9 misc 0 def assoc 3 seq 0 +// snd_hda: pin config default 0x00ab9030 port conn Jack loc ext Ext loc geom N/A default device Mic In conn type Combination color Pink misc undef def assoc 3 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 684); // 0x03cf000c +// snd_hda: 60 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 685); // 0x03cf0700 + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 688); // 0x03df0005 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 689); // 0x03df0009 +// snd_hda: 61 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 690); // 0x03df000f + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 691); // 0x03df0012 +// snd_hda: amp capabilities 61 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 692); // 0x03df000d +// snd_hda: amp capabilities 61 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 693); // 0x03df0009 +// snd_hda: 61 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 694); // 0x03df1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 695); // 0x03df000c +// snd_hda: 61 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3d, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 696); // 0x03df0700 + snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03d70700 +// snd_hda: 61 [] + + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 699); // 0x03ef0005 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 700); // 0x03ef0009 +// snd_hda: 62 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 701); // 0x03ef000f + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 702); // 0x03ef0012 +// snd_hda: amp capabilities 62 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 703); // 0x03ef000d +// snd_hda: amp capabilities 62 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 704); // 0x03ef0009 +// snd_hda: 62 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 705); // 0x03ef1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 706); // 0x03ef000c +// snd_hda: 62 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3e, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 707); // 0x03ef0700 + snd_hda_codec_write(codec, 0x3e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03e70700 +// snd_hda: 62 [] + + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 710); // 0x03ff0005 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 711); // 0x03ff0009 +// snd_hda: 63 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 712); // 0x03ff000f + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 713); // 0x03ff0012 +// snd_hda: amp capabilities 63 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 714); // 0x03ff000d +// snd_hda: amp capabilities 63 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 715); // 0x03ff0009 +// snd_hda: 63 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 716); // 0x03ff1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 717); // 0x03ff000c +// snd_hda: 63 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x3f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 718); // 0x03ff0700 + snd_hda_codec_write(codec, 0x3f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03f70700 +// snd_hda: 63 [] + + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 721); // 0x040f0005 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 722); // 0x040f0009 +// snd_hda: 64 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 723); // 0x040f000f + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 724); // 0x040f0012 +// snd_hda: amp capabilities 64 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 725); // 0x040f000d +// snd_hda: amp capabilities 64 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 726); // 0x040f0009 +// snd_hda: 64 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 727); // 0x040f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 728); // 0x040f000c +// snd_hda: 64 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x40, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 729); // 0x040f0700 + snd_hda_codec_write(codec, 0x40, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04070700 +// snd_hda: 64 [] + + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 732); // 0x041f0005 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 733); // 0x041f0009 +// snd_hda: 65 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 734); // 0x041f000f + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 735); // 0x041f0012 +// snd_hda: amp capabilities 65 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 736); // 0x041f000d +// snd_hda: amp capabilities 65 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 737); // 0x041f0009 +// snd_hda: 65 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 738); // 0x041f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 739); // 0x041f000c +// snd_hda: 65 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x41, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 740); // 0x041f0700 + snd_hda_codec_write(codec, 0x41, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04170700 +// snd_hda: 65 [] + + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 743); // 0x042f0005 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 744); // 0x042f0009 +// snd_hda: 66 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 745); // 0x042f000f + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 746); // 0x042f0012 +// snd_hda: amp capabilities 66 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 747); // 0x042f000d +// snd_hda: amp capabilities 66 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 748); // 0x042f0009 +// snd_hda: 66 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 749); // 0x042f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 750); // 0x042f000c +// snd_hda: 66 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x42, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 751); // 0x042f0700 + snd_hda_codec_write(codec, 0x42, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04270700 +// snd_hda: 66 [] + + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 754); // 0x043f0005 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 755); // 0x043f0009 +// snd_hda: 67 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 756); // 0x043f000f + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 757); // 0x043f0012 +// snd_hda: amp capabilities 67 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 758); // 0x043f000d +// snd_hda: amp capabilities 67 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00400001, 759); // 0x043f0009 +// snd_hda: 67 AC_WID_PIN ['AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_STEREO', 'AC_WCAP_TYPE'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 760); // 0x043f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 761); // 0x043f000c +// snd_hda: 67 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x43, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 762); // 0x043f0700 + snd_hda_codec_write(codec, 0x43, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04370700 +// snd_hda: 67 [] + + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 765); // 0x044f0005 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 766); // 0x044f0009 +// snd_hda: 68 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 767); // 0x044f000f + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 768); // 0x044f0012 +// snd_hda: amp capabilities 68 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 769); // 0x044f000d +// snd_hda: amp capabilities 68 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 770); // 0x044f0009 +// snd_hda: 68 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0xa0a60100, 771); // 0x044f1c00 +// snd_hda: pin config default 0xa0a60100 port conn 2 location 0x20 loc ext 2 loc geom 0 default device 10 conn type 6 color 0 misc 1 def assoc 0 seq 0 +// snd_hda: pin config default 0xa0a60100 port conn Fixed loc ext Sep loc geom N/A default device Mic In conn type Other Digital color Unknown misc Jack Detect Override def assoc 0 seq 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 772); // 0x044f000c +// snd_hda: 68 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 773); // 0x044f000d +// snd_hda: amp capabilities 68 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 774); // 0x044f0700 + snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 +// snd_hda: 68 [] + + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 777); // 0x045f0005 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 778); // 0x045f0009 +// snd_hda: 69 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 779); // 0x045f000f + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 780); // 0x045f0012 +// snd_hda: amp capabilities 69 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 781); // 0x045f000d +// snd_hda: amp capabilities 69 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x00000009, 0x0040000b, 782); // 0x045f0009 +// snd_hda: 69 AC_WID_PIN ['AC_WCAP_STEREO', 'AC_WCAP_AMP_OVRD', 'AC_WCAP_TYPE', 'AC_WCAP_DELAY', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_IN_AMP'] 0 0 4 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_CONFIG_DEFAULT, 0x00000000, 0x400000f0, 783); // 0x045f1c00 +// snd_hda: pin config default 0x400000f0 port conn 1 location 0x0 loc ext 0 loc geom 0 default device 0 conn type 0 color 0 misc 0 def assoc 15 seq 0 +// snd_hda: pin config default 0x400000f0 port conn NO CONN loc ext Ext loc geom N/A default device Line Out conn type Unknown color Unknown misc undef def assoc 15 seq 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000c, 0x00000020, 784); // 0x045f000c +// snd_hda: 69 ['AC_PINCAP_IN'] [] + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00270200, 785); // 0x045f000d +// snd_hda: amp capabilities 69 input 0x00270200 offset 0x00 numsteps 0x02 stepsize 0x27 mute 0 + retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 786); // 0x045f0700 + snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 +// snd_hda: 69 [] + + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 789); // 0x046f0005 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00700200, 790); // 0x046f0009 +// snd_hda: 70 AC_WID_BEEP ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_DIGITAL', 'AC_WCAP_TYPE'] 0 0 7 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 791); // 0x046f000f + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 792); // 0x046f0012 +// snd_hda: amp capabilities 70 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 793); // 0x046f000d +// snd_hda: amp capabilities 70 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x46, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00700200, 794); // 0x046f0009 +// snd_hda: 70 AC_WID_BEEP ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_DIGITAL', 'AC_WCAP_TYPE'] 0 0 7 + + snd_hda_codec_write(codec, 0x46, 0, AC_VERB_SET_BEEP_CONTROL, 0x00000000); // 0x04670a00 + +} + +static void read_vendor_node(struct hda_codec *codec) +{ + int retval; + + // I think this is also part of the node init loop + + // this may involve calls to AppleHDAWidgetCS8409::initForNodeID for each node + // and AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) for each node + + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 797); // 0x047f0005 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00f002c1, 798); // 0x047f0009 +// snd_hda: 71 AC_WID_VENDOR ['AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_UNSOL_CAP', 'AC_WCAP_DELAY', 'AC_WCAP_PROC_WID', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DIGITAL'] 0 0 15 + + // new addition June 2019 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 799); // 0x047f000f + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 800); // 0x047f0012 +// snd_hda: amp capabilities 71 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 801); // 0x047f000d +// snd_hda: amp capabilities 71 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00f002c1, 802); // 0x047f0009 +// snd_hda: 71 AC_WID_VENDOR ['AC_WCAP_STEREO', 'AC_WCAP_TYPE', 'AC_WCAP_UNSOL_CAP', 'AC_WCAP_DELAY', 'AC_WCAP_PROC_WID', 'AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DIGITAL'] 0 0 15 + +} + +static void read_coefs_all(struct hda_codec *codec); + +static void init_read_coefs(struct hda_codec *codec) +{ + + int retval; + + + // in AppleHDAWidget::initForNodeID(unsigned short, OSObject*, OSObject*) + + // get number of coefs in bits 15:8 - here 0x0000ff00 ie 255 + retval = snd_hda_codec_read_check(codec, CS8409_VENDOR_NID, 0, AC_VERB_PARAMETERS, 0x00000010, 0x0000ff00, 803); // 0x047f0010 + + + // this is after the read_all_nodes loop + + read_coefs_all(codec); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void read_coefs_all(struct hda_codec *codec) +{ + + // leave these in as check of state + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 804 ); // coef read 804 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 808 ); // coef read 808 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000080, 812 ); // coef read 812 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0003, 0x0000, 0x0000801f, 816 ); // coef read 816 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0004, 0x0000, 0x0000083f, 820 ); // coef read 820 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0005, 0x0000, 0x00000000, 824 ); // coef read 824 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0006, 0x0000, 0x00008000, 828 ); // coef read 828 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0007, 0x0000, 0x000028ff, 832 ); // coef read 832 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0008, 0x0000, 0x00000000, 836 ); // coef read 836 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x00000013, 840 ); // coef read 840 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000a, 0x0000, 0x00000000, 844 ); // coef read 844 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000b, 0x0000, 0x00000000, 848 ); // coef read 848 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000c, 0x0000, 0x0000cccc, 852 ); // coef read 852 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000d, 0x0000, 0x0000cccc, 856 ); // coef read 856 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000e, 0x0000, 0x00000000, 860 ); // coef read 860 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x000f, 0x0000, 0x0000aaaa, 864 ); // coef read 864 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0010, 0x0000, 0x0000cccc, 868 ); // coef read 868 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0011, 0x0000, 0x00000000, 872 ); // coef read 872 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0012, 0x0000, 0x0000cccc, 876 ); // coef read 876 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0013, 0x0000, 0x0000cccc, 880 ); // coef read 880 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0014, 0x0000, 0x00000000, 884 ); // coef read 884 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0015, 0x0000, 0x0000aaaa, 888 ); // coef read 888 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0016, 0x0000, 0x0000cccc, 892 ); // coef read 892 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0017, 0x0000, 0x00000000, 896 ); // coef read 896 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0018, 0x0000, 0x00000000, 900 ); // coef read 900 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000400, 904 ); // coef read 904 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00000420, 908 ); // coef read 908 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00008c20, 912 ); // coef read 912 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00008000, 916 ); // coef read 916 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001d, 0x0000, 0x00008c40, 920 ); // coef read 920 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001e, 0x0000, 0x00008000, 924 ); // coef read 924 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001f, 0x0000, 0x00008c60, 928 ); // coef read 928 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0020, 0x0000, 0x00008000, 932 ); // coef read 932 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0021, 0x0000, 0x00008000, 936 ); // coef read 936 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0022, 0x0000, 0x00008000, 940 ); // coef read 940 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0023, 0x0000, 0x00008000, 944 ); // coef read 944 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0024, 0x0000, 0x00008000, 948 ); // coef read 948 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0025, 0x0000, 0x00008000, 952 ); // coef read 952 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0026, 0x0000, 0x00008000, 956 ); // coef read 956 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0027, 0x0000, 0x00008000, 960 ); // coef read 960 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0028, 0x0000, 0x00008000, 964 ); // coef read 964 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00008000, 968 ); // coef read 968 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00008000, 972 ); // coef read 972 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002b, 0x0000, 0x00008000, 976 ); // coef read 976 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002c, 0x0000, 0x00008000, 980 ); // coef read 980 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002d, 0x0000, 0x00008000, 984 ); // coef read 984 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002e, 0x0000, 0x00008000, 988 ); // coef read 988 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002f, 0x0000, 0x00008000, 992 ); // coef read 992 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0030, 0x0000, 0x00008000, 996 ); // coef read 996 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0031, 0x0000, 0x00008000, 1000 ); // coef read 1000 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0032, 0x0000, 0x00008000, 1004 ); // coef read 1004 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0033, 0x0000, 0x00008000, 1008 ); // coef read 1008 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0034, 0x0000, 0x00008000, 1012 ); // coef read 1012 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0035, 0x0000, 0x00008000, 1016 ); // coef read 1016 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0036, 0x0000, 0x00008000, 1020 ); // coef read 1020 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0037, 0x0000, 0x00008000, 1024 ); // coef read 1024 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0038, 0x0000, 0x00008000, 1028 ); // coef read 1028 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0039, 0x0000, 0x00008000, 1032 ); // coef read 1032 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003a, 0x0000, 0x00008000, 1036 ); // coef read 1036 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003b, 0x0000, 0x00008000, 1040 ); // coef read 1040 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003c, 0x0000, 0x00008000, 1044 ); // coef read 1044 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003d, 0x0000, 0x00008000, 1048 ); // coef read 1048 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003e, 0x0000, 0x00008000, 1052 ); // coef read 1052 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x003f, 0x0000, 0x00008000, 1056 ); // coef read 1056 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0040, 0x0000, 0x00008000, 1060 ); // coef read 1060 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0041, 0x0000, 0x00008000, 1064 ); // coef read 1064 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0042, 0x0000, 0x00008000, 1068 ); // coef read 1068 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0043, 0x0000, 0x00008000, 1072 ); // coef read 1072 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0044, 0x0000, 0x00008000, 1076 ); // coef read 1076 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0045, 0x0000, 0x00008000, 1080 ); // coef read 1080 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0046, 0x0000, 0x00008000, 1084 ); // coef read 1084 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0047, 0x0000, 0x00008000, 1088 ); // coef read 1088 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0048, 0x0000, 0x00008000, 1092 ); // coef read 1092 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008000, 1096 ); // coef read 1096 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008000, 1100 ); // coef read 1100 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004b, 0x0000, 0x00008000, 1104 ); // coef read 1104 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004c, 0x0000, 0x00008000, 1108 ); // coef read 1108 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004d, 0x0000, 0x00008000, 1112 ); // coef read 1112 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004e, 0x0000, 0x00008000, 1116 ); // coef read 1116 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004f, 0x0000, 0x00008000, 1120 ); // coef read 1120 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0050, 0x0000, 0x00008000, 1124 ); // coef read 1124 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0051, 0x0000, 0x00008000, 1128 ); // coef read 1128 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0052, 0x0000, 0x00008000, 1132 ); // coef read 1132 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0053, 0x0000, 0x00008000, 1136 ); // coef read 1136 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0054, 0x0000, 0x00008000, 1140 ); // coef read 1140 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0055, 0x0000, 0x00008000, 1144 ); // coef read 1144 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0056, 0x0000, 0x00008000, 1148 ); // coef read 1148 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0057, 0x0000, 0x00008000, 1152 ); // coef read 1152 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0058, 0x0000, 0x00000400, 1156 ); // coef read 1156 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0059, 0x0000, 0x00000074, 1160 ); // coef read 1160 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005a, 0x0000, 0x0000007f, 1164 ); // coef read 1164 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005b, 0x0000, 0x00000010, 1168 ); // coef read 1168 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005c, 0x0000, 0x00000020, 1172 ); // coef read 1172 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005d, 0x0000, 0x00002d7f, 1176 ); // coef read 1176 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005e, 0x0000, 0x00002d7f, 1180 ); // coef read 1180 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005f, 0x0000, 0x00001600, 1184 ); // coef read 1184 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0060, 0x0000, 0x00000000, 1188 ); // coef read 1188 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0061, 0x0000, 0x00000000, 1192 ); // coef read 1192 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0062, 0x0000, 0x00000000, 1196 ); // coef read 1196 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0063, 0x0000, 0x00000000, 1200 ); // coef read 1200 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0064, 0x0000, 0x00000000, 1204 ); // coef read 1204 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0065, 0x0000, 0x00000000, 1208 ); // coef read 1208 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0066, 0x0000, 0x00000000, 1212 ); // coef read 1212 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0067, 0x0000, 0x00000000, 1216 ); // coef read 1216 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0068, 0x0000, 0x00000000, 1220 ); // coef read 1220 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0069, 0x0000, 0x00000000, 1224 ); // coef read 1224 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006a, 0x0000, 0x00000000, 1228 ); // coef read 1228 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006b, 0x0000, 0x00000000, 1232 ); // coef read 1232 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006c, 0x0000, 0x00000000, 1236 ); // coef read 1236 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006d, 0x0000, 0x00000000, 1240 ); // coef read 1240 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006e, 0x0000, 0x00000000, 1244 ); // coef read 1244 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x006f, 0x0000, 0x00000000, 1248 ); // coef read 1248 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0070, 0x0000, 0x00000000, 1252 ); // coef read 1252 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 1256 ); // coef read 1256 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0072, 0x0000, 0x00000000, 1260 ); // coef read 1260 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0073, 0x0000, 0x00000000, 1264 ); // coef read 1264 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0074, 0x0000, 0x00000000, 1268 ); // coef read 1268 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0075, 0x0000, 0x00000001, 1272 ); // coef read 1272 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0076, 0x0000, 0x00000009, 1276 ); // coef read 1276 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0077, 0x0000, 0x00000000, 1280 ); // coef read 1280 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0078, 0x0000, 0x00000000, 1284 ); // coef read 1284 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0079, 0x0000, 0x00000000, 1288 ); // coef read 1288 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007a, 0x0000, 0x00000000, 1292 ); // coef read 1292 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007b, 0x0000, 0x00000000, 1296 ); // coef read 1296 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007c, 0x0000, 0x00000000, 1300 ); // coef read 1300 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007d, 0x0000, 0x00000000, 1304 ); // coef read 1304 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007e, 0x0000, 0x00000000, 1308 ); // coef read 1308 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x007f, 0x0000, 0x00000000, 1312 ); // coef read 1312 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0080, 0x0000, 0x00000000, 1316 ); // coef read 1316 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0081, 0x0000, 0x00000000, 1320 ); // coef read 1320 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000ff00, 1324 ); // coef read 1324 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void read_virtual_widgets(struct hda_codec *codec) +{ + // setup the virtual widgets + + unsigned int retval; + + mycodec_info(codec, "command nid start read_virtual_widgets\n"); + + // copied to outer routine + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // moved to outer routine + //snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1335); // 0x048f0005 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1336); // 0x048f0009 +// snd_hda: 72 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1337); // 0x048f000f + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1338); // 0x048f000a +// snd_hda: pcm params 72 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1339); // 0x048f000b +// snd_hda: stream format params 72 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1340); // 0x048f0012 +// snd_hda: amp capabilities 72 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1341); // 0x048f000d +// snd_hda: amp capabilities 72 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x48, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1342); // 0x048f0009 +// snd_hda: 72 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1344); // 0x049f0005 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1345); // 0x049f0009 +// snd_hda: 73 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1346); // 0x049f000f + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1347); // 0x049f000a +// snd_hda: pcm params 73 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1348); // 0x049f000b +// snd_hda: stream format params 73 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1349); // 0x049f0012 +// snd_hda: amp capabilities 73 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1350); // 0x049f000d +// snd_hda: amp capabilities 73 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x49, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1351); // 0x049f0009 +// snd_hda: 73 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1353); // 0x04af0005 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1354); // 0x04af0009 +// snd_hda: 74 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1355); // 0x04af000f + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1356); // 0x04af000a +// snd_hda: pcm params 74 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1357); // 0x04af000b +// snd_hda: stream format params 74 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1358); // 0x04af0012 +// snd_hda: amp capabilities 74 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1359); // 0x04af000d +// snd_hda: amp capabilities 74 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1360); // 0x04af0009 +// snd_hda: 74 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000005, 0x00000000, 1362); // 0x04bf0005 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1363); // 0x04bf0009 +// snd_hda: 75 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + retval = snd_hda_codec_read_check(codec, 0x4a, 0, AC_VERB_PARAMETERS, 0x0000000f, 0x00000000, 1364); // 0x04bf000f + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000a, 0x00000000, 1365); // 0x04bf000a +// snd_hda: pcm params 75 bits: rates: + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000b, 0x00000000, 1366); // 0x04bf000b +// snd_hda: stream format params 75 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000012, 0x00000000, 1367); // 0x04bf0012 +// snd_hda: amp capabilities 75 output 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x0000000d, 0x00000000, 1368); // 0x04bf000d +// snd_hda: amp capabilities 75 input 0x00000000 offset 0x00 numsteps 0x00 stepsize 0x00 mute 0 + retval = snd_hda_codec_read_check(codec, 0x4b, 0, AC_VERB_PARAMETERS, 0x00000009, 0x00000000, 1369); // 0x04bf0009 +// snd_hda: 75 AC_WID_AUD_OUT ['AC_WCAP_CHAN_CNT_EXT', 'AC_WCAP_DELAY', 'AC_WCAP_TYPE'] 0 0 0 + + + mycodec_info(codec, "command nid end read_virtual_widgets\n"); + +} + +static void setup_jack_pin_config(struct hda_codec *codec) +{ + + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // this is likely some call of setPinConfigDefault + // 0x45 -> 0x23 (macbook pro) is the line in path - so why does it say its a mike?? + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, 0x00000001); // 0x04571c01 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0x00000001); // 0x04571d01 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x000000a0); // 0x04571ea0 + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x00000090); // 0x04571f90 +// snd_hda: pin config 0x90a00101 port conn 2 location 0x10 loc ext 1 loc geom 0 default device 10 conn type 0 color 0 misc 1 def assoc 0 seq 1 +// snd_hda: pin config 0x90a00101 port conn Fixed loc ext Int loc geom N/A default device Mic In conn type Unknown color Unknown misc Jack Detect Override def assoc 0 seq 1 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void enable_i2c(struct hda_codec *codec) +{ + //int retval; + + // AppleBusControllerCS8409::init + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + +// snd_hda: # AppleHDAFunctionGroupCS8409::enableI2CClock: + // codes from windows cs4208_38.inf file + // 0x9008 = PLL1_EN(0x1000),I2C_EN(0x8) + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1691 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1691 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9008, 0x00000000, 1695 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1695 + + // 0x0002 0x0080 +GPIO_I2C (0x80) not used ASP1_BUS_IDLE = 0x200 ASP2_BUS_IDLE = 0x800 (0b1010) ASP1/2_BUS_IDLE = 10 + // 0x005b undocumented + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000000, 1699 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1699 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0002, 0x0080, 0x00000000, 1703 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1703 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000080, 1707 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1707 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x005b, 0x0000, 0x00000010, 1711 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1711 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x005b, 0x0010, 0x00000000, 1715 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1715 + +// snd_hda: # AppleHDAFunctionGroupCS8409::enableI2CClock: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1719 ); // AppleHDAFunctionGroupCS8409::enableI2C coef read 1719 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9000, 0x00000000, 1723 ); // AppleHDAFunctionGroupCS8409::enableI2C coef write 1723 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void init_for_node_vendor(struct hda_codec *codec) +{ + //int retval; + int coef_idx = 0x1a; + //int coef_ret = 0; + + // this is AppleHDAFunctionGroupCS8409::initForNodeID + + mycodec_info(codec, "command init_for_node_vendor start\n"); + + // these 2 items seem to enable the i2c clock - but we havent enabled i2c yet!! + // whats the difference?? + // Im certain this is enabling the i2c clock + // codes from windows cs4208_38.inf file + // 0x9008 = PLL1_EN(0x1000),I2C_EN(0x8) + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009008, 1371 ); // coef read 1371 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9008, 0x00000000, 1375 ); // coef write 1375 + + // clear these coefs + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 1379 ); // coef read 1379 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000000, 1383 ); // coef write 1383 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0017, 0x0000, 0x00000000, 1387 ); // coef write 1387 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0018, 0x0000, 0x00000000, 1391 ); // coef write 1391 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0002, 0x0000, 0x00000000, 1395 ); // coef write 1395 + + //coef_ret = cs_8409_vendor_coef_set(codec, 0x19, 0x8000); + cs_8409_vendor_coef_set(codec, 0x19, 0x8000); + + //if (coef_ret == -1) error; + + while (coef_idx <= 0x57) + { + //coef_ret = cs_8409_vendor_coef_set(codec, coef_idx, 0x8000); + cs_8409_vendor_coef_set(codec, coef_idx, 0x8000); + + //if (coef_ret == -1) error; + + coef_idx++; + } + + // 0x82 0x0000 ASP1/2_xxx_EN = 0, DMIC1/2_SCL_EN = 0 + // others not documented cs4208_38.inf + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x000a, 0x0000, 0x00000000, 1651 ); // coef write 1651 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00000000, 1655 ); // coef write 1655 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 1659 ); // coef write 1659 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 1663 ); // coef write 1663 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00000000, 1667 ); // coef write 1667 + + + mycodec_info(codec, "command init_for_node_vendor end\n"); + +} + +static void determine_speaker_id(struct hda_codec *codec) +{ + int retval; + + // this is determineSpeakerID + // this does not make sense - this just checks a GPIO pin?? + + mycodec_info(codec, "command determine_speaker_id start\n"); + + // this is call AppleHDAFunctionGroup::setGPIOEnable in determineSpeakerID + + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + { + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000008); // 0x00171608 +// snd_hda: gpio enable 1 0x08 + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000000, 1672); // 0x001f1500 +// snd_hda: gpio data 1 0x00 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000000c); // 0x0017160c +// snd_hda: gpio enable 1 0x0c + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000000, 1674); // 0x001f1500 +// snd_hda: gpio data 1 0x00 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000008); // 0x00171608 + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0x8 data 0x%08x\n", retval); + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000000c); // 0x0017160c + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0xc data 0x%08x\n", retval); + + } + else + { + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000004); // 0x00171604 +// snd_hda: gpio enable 1 0x04 + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000, 0x00000004, 1672); // 0x001f1500 +// snd_hda: gpio data 1 0x04 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x04); // 0x00171604 + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command determine_speaker_id gpio mask 0x4 data 0x%08x\n", retval); + + } + + mycodec_info(codec, "command determine_speaker_id end\n"); + +} + + +static void enable_GPIforUR(struct hda_codec *codec, int mask) +{ + int retval; + + + // AppleHDAFunctionGroupCS8409::enableGPIforUR + + mycodec_info(codec, "command enable_GPIforUR start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + // this is AppleHDAFunctionGroup::setGPIOEnable in AppleHDAFunctionGroup::enableGPIforUR + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000005); // 0x00171605 +// snd_hda: gpio enable 1 0x05 + + // this is AppleHDAFunctionGroup::setGPIODirection in AppleHDAFunctionGroup::enableGPIforUR + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000000); // 0x00171700 +// snd_hda: gpio direction 1 0x00 in in in in in in in in + + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_WAKE_MASK, 0x00000000, 0x00000000, 1735); // 0x001f1800 +// snd_hda: gpio wake enable 1 0x00 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_WAKE_MASK, 0x00000001); // 0x00171801 +// snd_hda: gpio wake enable 1 0x01 + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0x00000000, 0x00000000, 1737); // 0x001f1900 +// snd_hda: gpio unsol enable 1 0x00 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x00000001); // 0x00171901 +// snd_hda: gpio unsol enable 1 0x01 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171605 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000000); // 0x00171700 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_WAKE_MASK, 0x00000000, 0x00000000, 1735); // 0x001f1800 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_WAKE_MASK, 0x00000001); // 0x00171801 + + retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0x00000000, 0x00000000, 1737); // 0x001f1900 + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x00000001); // 0x00171901 + + + // likely last runVerb of AppleHDAFunctionGroupCS8409::enableGPIforUR + snd_hda_codec_write(codec, codec->core.afg, 0, 0x7f0, 0x000000b7); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command enable_GPIforUR end\n"); + +} + + // this may be more likely + // only issue is that AppleHDATDM_Codec::resetDevice seems to have other functions that seem likely to send i2c data + // note the setExternalControlForDefaultDeviceTypeAndTag function sends the GPIO updates as noted above + // but AppleHDATDM_CS42L83::resetDevice contains the 0x1301 to 0x130f sequence + // AppleHDATDM_CS42L83::init + // AppleHDATDM_CS42L83::resetDevice + // AppleHDATDM_Codec::resetDevice + // AppleHDAFunctionGroup::externalControlExistsForDefaultDeviceTypeAndTag(0xf(%rsi), 0x35(%rdx)) + // AppleHDAFunctionGroup::setExternalControlForDefaultDeviceTypeAndTag(1(%rsi), 0xf(%rdx), 0x35(%rcx)) + // AppleHDAFunctionGroupExternalControl::setExternalControlState(unk(%rsi), unk(%rdx)) + // AppleHDAFunctionGroup::setExternalControlForDefaultDeviceTypeAndTag(0(%rsi), 0xf(%rdx), 0x35(%rcx)) + // AppleHDAFunctionGroupExternalControl::setExternalControlState(unk(%rsi), unk(%rdx)) + + // AppleHDAFunctionGroupExternalControl::setExternalControlState + // calls AppleHDAFunctionGroupExternalControl::publicSetExternalControlState + // this appears to be virtual - the definition just asserts + // good candidate is AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState + // AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState + // is the function which does the external_control_GPIO_clear0 or external_control_GPIO_set1 + // functions below + + // followed by i2c calls for 0x1301 to 0x130f + // we have a 2nd set of GPIO clear/set calls which cant figure out where they get called + // followed by i2c calls 0x1001 + // followed by i2c calls 0x1005 + // followed by AppleHDATDM_CS42L83::_initHW() + + +static void cs42l83_external_control_GPIO_clear_2(struct hda_codec *codec,int mask); +static void cs42l83_external_control_GPIO_set_2(struct hda_codec *codec,int mask); + +static void cs42l83_external_control_GPIO(struct hda_codec *codec,int mask) +{ + + // from AppleHDATDM_Codec::resetDevice + + // I think this is resetting the device by pulling some line low then high + // GPIO 2 is for the cs42l83 + + // note that AppleHDAFunctionGroupExternalControl::setExternalControlState + // has an IOSleep() as the first call depending on a value - if non-zero time to sleep + // if 0 IOSleep() call ignored + + mycodec_info(codec, "command cs42l83_external_control_GPIO start\n"); + + // this clearing then setting gpio bit 2 + + //usleep_range(2000,4000); + + cs42l83_external_control_GPIO_clear_2(codec,mask); + + //usleep_range(2000,4000); + + cs42l83_external_control_GPIO_set_2(codec,mask); + + + mycodec_info(codec, "command cs42l83_external_control_GPIO end\n"); + +} + + +static void cs42l83_external_control_GPIO_clear_2(struct hda_codec *codec,int mask) +{ + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // AppleHDAFunctionGroup::setGPIODirection + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 +// snd_hda: gpio direction 1 0x02 in in in in in in out in + // AppleHDAFunctionGroup::setGPIOData + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000000); // 0x00171500 +// snd_hda: gpio data 1 0x00 + // AppleHDAFunctionGroup::setGPIOEnable + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000007); // 0x00171607 +// snd_hda: gpio enable 1 0x07 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000000); // 0x00171500 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171607 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + +static void cs42l83_external_control_GPIO_set_2(struct hda_codec *codec,int mask) +{ + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // AppleHDAFunctionGroup::setGPIODirection + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 +// snd_hda: gpio direction 1 0x02 in in in in in in out in + // AppleHDAFunctionGroup::setGPIOData + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000002); // 0x00171502 +// snd_hda: gpio data 1 0x02 + // AppleHDAFunctionGroup::setGPIOEnable + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000007); // 0x00171607 +// snd_hda: gpio enable 1 0x07 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000002); // 0x00171702 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000002); // 0x00171502 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, mask); // 0x00171607 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void cs42l83_reset(struct hda_codec *codec) +{ + // AppleHDATDM_CS42L83::resetDevice() + + // these are i2c calls + // so it seems Apple is using SET_COEF_INDEX, SET_PROC_COEF to write to the local i2c bus + // writing to coef index 0x59 for the i2c address, reading from 0x5c for i2c status + // and writing to 0x5d for i2c register, data writes (8 bit each in the 16 bit coef data with register in hi byte) + // and writing/reading to 0x5e for i2c register, data reads - the write is register with lo byte 0x00 + // and read is data for i2c register (i2c register returned) with data in lo byte + + // based on info from bugs 195671 and 110561 + // and that writing to coef index 0x59 seems to be the i2c address hence the i2c address is 0x90 + // this is likely setting up the headphone/mic jack ic CS42L83A + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_reset end\n"); +} + +static int cs42l83_device_id(struct hda_codec *codec) +{ + int retval_dev; + int retval_rev; + int ret_id; + + // in AppleHDATDM_CS42L83::init + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0100 i2c data 0x0142 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0500 i2c data 0x05b0 + + retval_dev = cs_8409_vendor_i2cRead(codec, 0x90, 0x1001, 1); // snd_hda + retval_rev = cs_8409_vendor_i2cRead(codec, 0x90, 0x1005, 1); // snd_hda + + ret_id = ((retval_dev & 0xff) << 16) | (retval_rev & 0xff); + + mycodec_info(codec, "command cs42l83_device_id end\n"); + + return ret_id; +} + +static void cs42l83_inithw(struct hda_codec *codec) +{ + + // AppleHDATDM_CS42L83::_initHW() called from AppleHDATDM_CS42L83::init + + // this triggers the 1st UNSOL response + + mycodec_info(codec, "command cs42l83_inithw start\n"); + + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0300 i2c data 0x0320 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0322 i2c data 0x0022 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2901 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x200f i2c data 0x000f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b03 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x023f i2c data 0x003f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x21a6 i2c data 0x00a6 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1600 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1800 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1900 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1a00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1c00 i2c data 0x1c7f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1c1a i2c data 0x001a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1e00 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1f00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x01fe i2c data 0x00fe +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x751f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x759f i2c data 0x009f + + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1103, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1103, 0x0022, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1129, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1320, 0x000f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2302, 0x003f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f01, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1121, 0x00a6, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1316, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1317, 0x0000, 1); // snd_hda + + // this triggers an UNSOL response with interrupt cleared state + // this writes to SRC Interrupt Mask unmasking all interrupts + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1318, 0x0000, 1); // snd_hda + + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1319, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131a, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131c, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131c, 0x001a, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131e, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131f, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + // the following is setting the power register + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0xff to 0xfe (Codec power up) + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x00fe, 1); // snd_hda + + // the following is setting the mic detect register + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x1f ((0x80 LATCH_TO_VP unset, HS_DETECT_LEVEL 1f) to 0x9f (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x1f) + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x009f, 1); // snd_hda + + + mycodec_info(codec, "command cs42l83_inithw end\n"); +} + +static void setup_gpio_set_20(struct hda_codec *codec); + +static void setup_amps_reset_i2c_max(struct hda_codec *codec) +{ + // based on info from bugs 195671 and 110561 + // and that writing to coef index 0x59 seems to be the i2c address hence the i2c address following are + // the amps 62, 64, 72, 74 + // also the data being sent does correpond to the registers of the MAX98372 from data sheet + // which is supposedly similar to the actual MAX98706 + // all analysis of the i2cWrite data is based on the MAX98372 data sheet + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x64, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x62 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x62, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x74 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x74, 0x0051, 0x0001, 0); // snd_hda + + setup_gpio_set_20(codec); + +// snd_hda i2cWrite i2c address 0x72 i2c reg 0x5101 i2c data 0x0001 reg anal: SoftwareReset : Reset + cs_8409_vendor_i2cWrite(codec, 0x72, 0x0051, 0x0001, 0); // snd_hda + +} + +static void setup_gpio_set_20(struct hda_codec *codec) +{ + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // I think this may be associated with the amps + // - this is a difference between the MBP 13,1 and SSM amps and 14,3 and MAX amps + // its either applying power to the amps or taking them out of reset + // (note that unlike GPIO 2 there doesnt seem to be a clear 20) + + mycodec_info(codec, "command setup_gpio_set_20 start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000022); // 0x00171722 +// snd_hda: gpio direction 1 0x22 in in out in in in out in + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000022); // 0x00171522 +// snd_hda: gpio data 1 0x22 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000027); // 0x00171627 +// snd_hda: gpio enable 1 0x27 + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000022); // 0x00171722 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000022); // 0x00171522 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x00000027); // 0x00171627 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command setup_gpio_set_20 end\n"); +} + + +static void setup_amps_reset_i2c_ssm3(struct hda_codec *codec) +{ + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x28 i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2a i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2c i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled +// snd_hda i2cWrite i2c address 0x2e i2c reg 0x0083 i2c data 0x0083 reg anal: PowerControl : PowerDown SoftwareReset BVSenseOn AutoPwrOffEnabled + + cs_8409_vendor_i2cWrite(codec, 0x28, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2a, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2c, 0x0000, 0x0083, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x2e, 0x0000, 0x0083, 1); // snd_hda + +} + + +static void setup_gpio_set_10(struct hda_codec *codec); + +static void setup_amps_reset_i2c_tas576(struct hda_codec *codec) +{ + // the Texas TAS5764 is undocumented - the nearest seems to be the TAS5760md + // which has some similarities but some discrepancies + // for the moment using the TAS5760md registers where they seem to be similar + // based on info that writing to coef index 0x59 seems to be the i2c address hence the amps i2c address following are + // 0xd8, 0xda, 0xdc, 0xde + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xda i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xda i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xda i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xda i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xda i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xda i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xda i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0014, 0x0002, 0); // snd_hda + + setup_gpio_set_10(codec); + +// snd_hda i2cWrite i2c address 0xde i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not Sleep, Spkr Amp Shutdown +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0204 i2c data 0x0004 reg anal: Digital Control : I2S format +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0380 i2c data 0x0080 reg anal: Volume Control : Fade +// snd_hda i2cWrite i2c address 0xde i2c reg 0x04cf i2c data 0x00cf reg anal: Left Chan Vol Control : 0dB +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0651 i2c data 0x0051 reg anal: Analog Control : PWM Rate x16 +// snd_hda i2cWrite i2c address 0xde i2c reg 0x0800 i2c data 0x0000 reg anal: Fault Config & Error +// snd_hda i2cWrite i2c address 0xde i2c reg 0x10ff i2c data 0x00ff reg anal: Digital Clipper +// snd_hda i2cWrite i2c address 0xde i2c reg 0x11fc i2c data 0x00fc reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xde i2c reg 0x1300 i2c data 0x0000 reg anal: Undocumented +// snd_hda i2cWrite i2c address 0xde i2c reg 0x1402 i2c data 0x0002 reg anal: Undocumented + + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0001, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0002, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0003, 0x0080, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0004, 0x00cf, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0006, 0x0051, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0008, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0010, 0x00ff, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0011, 0x00fc, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0013, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0014, 0x0002, 0); // snd_hda + +} + +static void setup_gpio_set_10(struct hda_codec *codec) +{ + + // plausibly AppleHDAFunctionGroupExternalControl_GPIO::publicSetExternalControlState(bool) + + // I think this may be associated with the amps + // its either applying power to the amps or taking them out of reset + // (note that unlike GPIO 2 there doesnt seem to be a clear 10) + + // note that this seems to be following the MAX style setup + + mycodec_info(codec, "command setup_gpio_set_10 start\n"); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000012); // 0x00171712 +// snd_hda: gpio direction 1 0x12 in in in out in in out in + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000012); // 0x00171512 +// snd_hda: gpio data 1 0x12 + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000001f); // 0x0017161f +// snd_hda: gpio enable 1 0x1f + + + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 0x00000012); // 0x00171712 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0x00000012); // 0x00171512 + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 0x0000001f); // 0x0017161f + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "command setup_gpio_set_10 end\n"); +} + + +static void cs42l83_mic_detect(struct hda_codec *codec) +{ + int retval; + int newval; + + int detect_standby = 0; + + // likely from AppleHDAMikeyInternalCS8409::setupJackDetection + // which does call enterStandby with 0 arg + + // enterStandby is only function that issues 0x75 but it doesnt look right + // mic detect is register name + + // AppleHDATDM_CS42L83::enterStandby + + // so this appears to change 0x1b75 from 0x9f to 0x9f + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) to 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // curious - these functions not separated by power down/ups + // ah - the log may not be ordered - they are output as a bunch at the end!! + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x759f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x759f i2c data 0x009f + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + + if (detect_standby) + newval = (retval & 0x7f); + else + newval = (retval | 0x80); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x009f, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_mic_detect end\n"); +} + +static void cs42l83_tip_sense(struct hda_codec *codec, int invert) +{ + int retval; + int newval1; + int newval2; + int newval; + //int invert = 0; + + // likely in AppleHDAMikeyInternalCS8409::setupJackDetection + // - only 0x73 readMikey/writeMikey calls seen + + // register 0x1b73 - Tip Sense Control 2 + // changed from 0x02 (default) to 0xc0 (short detect) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7300 i2c data 0x7302 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x73c0 i2c data 0x00c0 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b73, 1); // snd_hda + + // invert indicates inverted signal path for physical jack presence detect circuit + + // following code translated from assembler + newval1 = (retval & 0x1c); + if (invert) + newval = (newval1 | 0xe0); + else + newval = (newval1 | 0xc0); + + + if (invert) + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b73, 0x00e0, 1); // snd_hda + else + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b73, 0x00c0, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_tip_sense end\n"); +} + + diff --git a/patch_cirrus_hda_generic_copy.h b/patch_cirrus_hda_generic_copy.h new file mode 100644 index 0000000..17b3ffd --- /dev/null +++ b/patch_cirrus_hda_generic_copy.h @@ -0,0 +1,432 @@ + +/* playback mute control with the software mute bit check */ +static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + if (spec->auto_mute_via_amp) { + hda_nid_t nid = get_amp_nid(kcontrol); + bool enabled = !((spec->mute_bits >> nid) & 1); + ucontrol->value.integer.value[0] &= enabled; + ucontrol->value.integer.value[1] &= enabled; + } +} + + +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} + + +/* + * Bound mute controls + */ +#define AMP_VAL_IDX_SHIFT 19 +#define AMP_VAL_IDX_MASK (0x0f<<19) + +static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} + +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int i, indices, err = 0, change = 0; + + sync_auto_mute_bits(kcontrol, ucontrol); + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; + for (i = 0; i < indices; i++) { + kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | + (i << AMP_VAL_IDX_SHIFT); + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err < 0) + break; + change |= err; + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err < 0 ? err : change; +} + + +enum { + HDA_CTL_WIDGET_VOL, + HDA_CTL_WIDGET_MUTE, + HDA_CTL_BIND_MUTE, +}; + + +static const struct snd_kcontrol_new control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + /* only the put callback is replaced for handling the special mute */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = hda_gen_mixer_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_hda_mixer_amp_switch_info, + .get = hda_gen_bind_mute_get, + .put = hda_gen_bind_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, +}; + + +/* add the powersave loopback-list entry */ +static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + list = snd_array_new(&spec->loopback_list); + if (!list) + return -ENOMEM; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->loopback.amplist = spec->loopback_list.list; + return 0; +} + + +static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + + + +//static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) +//{ +// struct hda_gen_spec *spec = codec->spec; +// struct snd_kcontrol_new *knew; +// char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +// unsigned int defcfg; +// +// if (pin == spec->hp_mic_pin) +// return 0; /* already done in create_out_jack_mode() */ +// +// /* no jack mode for fixed pins */ +// defcfg = snd_hda_codec_get_pincfg(codec, pin); +// if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) +// return 0; +// +// /* no multiple vref caps? */ +// if (get_in_jack_num_items(codec, pin) <= 1) +// return 0; +// +// get_jack_mode_name(codec, pin, name, sizeof(name)); +// knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); +// if (!knew) +// return -ENOMEM; +// knew->private_value = pin; +// return 0; +//} + + +static void print_nid_path(struct hda_codec *codec, + const char *pfx, struct nid_path *path) +{ + char buf[40]; + char *pos = buf; + int i; + + *pos = 0; + for (i = 0; i < path->depth; i++) + pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x", + pos != buf ? ":" : "", + path->path[i]); + + codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf); +} + + +/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +} + + +/* fill the label for each input at first */ +static int fill_input_pin_labels(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *label; + int j, idx; + + if (!is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + idx = 0; + for (j = i - 1; j >= 0; j--) { + if (spec->input_labels[j] && + !strcmp(spec->input_labels[j], label)) { + idx = spec->input_label_idxs[j] + 1; + break; + } + } + + spec->input_labels[i] = label; + spec->input_label_idxs[i] = idx; + } + + return 0; +} + +/* Parse the codec tree and retrieve ADCs */ +static int fill_adc_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int nums = 0; + + for_each_hda_codec_node(nid, codec) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + if (++nums >= max_nums) + break; + } + spec->num_adc_nids = nums; + + codec_dbg(codec, "fill_adc_nids num nids %d\n",nums); + + /* copy the detected ADCs to all_adcs[] */ + spec->num_all_adcs = nums; + memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); + + return nums; +} + + +#define update_pin_ctl(codec, pin, val) \ + snd_hda_codec_write_cache(codec, pin, 0, \ + AC_VERB_SET_PIN_WIDGET_CONTROL, val) + +/* set the pinctl target value and write it if requested */ +static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool do_write) +{ + if (!pin) + return; + val = snd_hda_correct_pin_ctl(codec, pin, val); + snd_hda_codec_set_pin_target(codec, pin, val); + if (do_write) + update_pin_ctl(codec, pin, val); +} + + + +/* nid, dir and idx */ +#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) + +/* check whether the given ctl is already assigned in any path elements */ +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) +{ + struct hda_gen_spec *spec = codec->spec; + const struct nid_path *path; + int i; + + val &= AMP_VAL_COMPARE_MASK; + snd_array_for_each(&spec->paths, i, path) { + if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) + return true; + } + return false; +} + +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int type) +{ + unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + return is_ctl_used(codec, val, type); +} + + +/* return true if either a volume or a mute amp is found for the given + * aamix path; the amp has to be either in the mixer node or its direct leaf + */ +static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid, + hda_nid_t pin, unsigned int *mix_val, + unsigned int *mute_val) +{ + int idx, num_conns; + const hda_nid_t *list; + hda_nid_t nid; + + idx = snd_hda_get_conn_index(codec, mix_nid, pin, true); + if (idx < 0) + return false; + + *mix_val = *mute_val = 0; + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) + *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) + *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + if (*mix_val && *mute_val) + return true; + + /* check leaf node */ + num_conns = snd_hda_get_conn_list(codec, mix_nid, &list); + if (num_conns < idx) + return false; + nid = list[idx]; + if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) && + !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL)) + *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) && + !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL)) + *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + + return *mix_val || *mute_val; +} + + + + +/* add dynamic controls from template */ +static struct snd_kcontrol_new * +add_control(struct hda_gen_spec *spec, int type, const char *name, + int cidx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); + if (!knew) + return NULL; + knew->index = cidx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + if (knew->access == 0) + knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + knew->private_value = val; + return knew; +} + + +static int add_control_with_pfx(struct hda_gen_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, int cidx, unsigned long val) +{ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int len; + + len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + if (snd_BUG_ON(len >= sizeof(name))) + return -EINVAL; + if (!add_control(spec, type, name, cidx, val)) + return -ENOMEM; + return 0; +} + + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) + + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct hda_codec *codec, int input_idx, + hda_nid_t pin, const char *ctlname, int ctlidx, + hda_nid_t mix_nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int mix_val, mute_val; + int err, idx; + + if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val)) + return 0; + + path = snd_hda_add_new_path(codec, pin, mix_nid, 0); + if (!path) + return -EINVAL; + print_nid_path(codec, "loopback", path); + spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); + + idx = path->idx[path->depth - 1]; + if (mix_val) { + err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val); + if (err < 0) + return err; + path->ctls[NID_PATH_VOL_CTL] = mix_val; + } + + if (mute_val) { + err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val); + if (err < 0) + return err; + path->ctls[NID_PATH_MUTE_CTL] = mute_val; + } + + path->active = true; + path->stream_enabled = true; /* no DAC/ADC involved */ + err = add_loopback_list(spec, mix_nid, idx); + if (err < 0) + return err; + + if (spec->mixer_nid != spec->mixer_merge_nid && + !spec->loopback_merge_path) { + path = snd_hda_add_new_path(codec, spec->mixer_nid, + spec->mixer_merge_nid, 0); + if (path) { + print_nid_path(codec, "loopback-merge", path); + path->active = true; + path->pin_fixed = true; /* static route */ + path->stream_enabled = true; /* no DAC/ADC involved */ + spec->loopback_merge_path = + snd_hda_get_path_idx(codec, path); + } + } + + return 0; +} + diff --git a/patch_cirrus_new84.h b/patch_cirrus_new84.h new file mode 100644 index 0000000..1eeb183 --- /dev/null +++ b/patch_cirrus_new84.h @@ -0,0 +1,1906 @@ + + +// this sets the power state of the AFG node - ie node 0x1 +// this calls hda_sync_power_state +//hda_set_power_state(codec, AC_PWRST_D0); + +// this checks the node has reached the requested power state +//state = hda_sync_power_state(codec, nid, power_state); +// + + +// pigs need local definition as this is a static local function + +/* + * wait until the state is reached, returns the current state + */ +static unsigned int hda_sync_power_state_8409(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + unsigned long end_time = jiffies + msecs_to_jiffies(500); + unsigned int state, actual_state; + mycodec_info(codec, "hda_sync_power_state_8409 to 0x%04x\n",power_state); + + for (;;) { + state = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state & AC_PWRST_ERROR) + break; + actual_state = (state >> 4) & 0x0f; + if (actual_state == power_state) + break; + if (time_after_eq(jiffies, end_time)) + break; + /* wait until the codec reachs to the target state */ + msleep(1); + } + mycodec_info(codec, "hda_sync_power_state_8409 power state 0x%04x\n",state); + return state; +} + +// pigs - need my own power state +// Apple seems to set node 0x01 - the AFG - primarily +// hda_set_power_state sets all nodes to the required power state +// so apparently node 0x01 does not have the power capability - but is powerable!! +// if we wish to use this for all nodes then need to check for this + +static unsigned int hda_set_node_power_state_dbg(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state, bool dbgflg) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int state = power_state; + //unsigned int current_state; + if (dbgflg) mycodec_info(codec, "hda_set_node_power_state nid 0x%02x power %d\n",nid,power_state); + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + if (!(state & AC_PWRST_ERROR)) { + if (state != power_state) { + if (nid == 0x01 || (wcaps & AC_WCAP_POWER)) { + if (nid != 0x01 && codec->power_filter) { + state = codec->power_filter(codec, nid, power_state); + // ah - this is for preventing a node from being turned off + // we are not in AC_PWRST_D3 but we are requesting AC_PWRST_D3 + // (Im assuming we assume if not in AC_PWRST_D3 we are in AC_PWRST_D0 + if (state != power_state && power_state == AC_PWRST_D3) + {} + else + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + } + else + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + + state = hda_sync_power_state_8409(codec, nid, power_state); + } + else + dev_info(hda_codec_dev(codec), "hda_set_node_power_state no power cap!!\n"); + } + } + else { + dev_info(hda_codec_dev(codec), "hda_set_node_power_state ERROR!! nid 0x%02x 0x%04x\n",nid, state); + } + if (dbgflg) mycodec_info(codec, "hda_set_node_power_state end power %d\n",state); + + return state; +} + +static unsigned int hda_set_node_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) +{ + return hda_set_node_power_state_dbg(codec, nid, power_state, 0); +} + +static unsigned int hda_set_node_power_state_simple(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) +{ + unsigned int state = power_state; + //unsigned int current_state; + mycodec_info(codec, "hda_set_node_power_state_simple power %d\n",power_state); + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + if (!(state & AC_PWRST_ERROR)) { + if (state != power_state) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); + state = hda_sync_power_state_8409(codec, nid, power_state); + } + } + mycodec_info(codec, "hda_set_node_power_state_simple end power %d\n",state); + + return state; +} + + +static void hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, int flagint) +{ + unsigned int state; + state = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + mycodec_info(codec, "hda_check_power_state nid 0x%02x power 0x%04x %d\n", nid, state, flagint); +} + + +// go with Apple way?? +// this always does a get with index 0 initially and terminates with a set to 0 finally + +static inline unsigned int cs_8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + struct cs8409_apple_spec *spec = codec->spec; + unsigned int retval; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + retval = snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + return retval; +} + +static inline void cs_8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + struct cs8409_apple_spec *spec = codec->spec; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_PROC_COEF, coef); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + // appears to return 0 +} + +static inline unsigned int cs_8409_vendor_coef_set_mask(struct hda_codec *codec, unsigned int idx, + unsigned int coef, unsigned int mask, unsigned int srcval, int srcidx) +{ + // for the moment hackily add srcidx argument while debugging + struct cs8409_apple_spec *spec = codec->spec; + unsigned int retval; + unsigned int mask_coef; + snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + retval = snd_hda_codec_read(codec, CS8409_VENDOR_NID, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, idx); + mask_coef = (retval & ~mask) | coef; + if (srcval != 0) + { + if (srcidx != 0 && mask_coef != srcval) + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) 0x%04x != 0x%04x %d BAD",idx,coef,mask_coef,retval,coef,mask, mask_coef, srcval, srcidx); + else + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",idx,coef,mask_coef,retval,coef,mask,srcidx); + } + else + //if (mask != 0xffff) + myprintk_dbg("snd_hda_intel: cs_8409_vendor_coef_set_mask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",idx,coef,mask_coef,retval,coef,mask,srcidx); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_PROC_COEF, mask_coef); + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, + AC_VERB_SET_COEF_INDEX, 0); + // appears to return 0 + // lets return the read value for checking + return retval; +} + +static inline void cs_8409_vendor_enableI2Cclock(struct hda_codec *codec, unsigned int flag) +{ + + unsigned int retval = 0; + unsigned int newval = 0; + + // note that apple returns the status value with data value in returned parameter + // snd_hda_codec_read just returns value - not sure what happens about errors + // looks as tho its assumed -1 is not a valid return value + // ah yes - because max val is 16 bit quantity + + retval = cs_8409_vendor_coef_get(codec, 0x0); + //if (retval == -1) + + if (retval == -1) + return; + + newval = retval; + if (flag) + newval |= 0x8; + else + newval = (retval & 0xfffffff7); + + cs_8409_vendor_coef_set(codec, 0x0, newval); + +} + + +// define i2cRead and i2cWrite functions +// following Apple +static unsigned int cs_8409_vendor_i2cRead(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int paged) +{ + // AppleHDAFunctionGroupCS8409::_i2cRead(bool, unsigned short, unsigned short, unsigned int*) + // note that last argument is return data + unsigned int i2c_reg_data; + unsigned int retval; + int rdcnt; + + myprintk_dbg("snd_hda_intel: i2cRead 0x%04x 0x%04x: %d",i2c_address,i2c_reg,paged); + + hda_set_node_power_state_dbg(codec, codec->core.afg, AC_PWRST_D0, 0); + // exit on error + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); + // exit on error + + cs_8409_vendor_enableI2Cclock(codec, 0x1); + + + cs_8409_vendor_coef_set(codec, 0x59, i2c_address); + + if (paged) + { + unsigned int retval1; + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg >> 8); + + rdcnt = -8; +sleep1: + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + if (retval1 != -1) + { + retval1 &= 0x18; + if (retval1 != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep1; + } + } + } + } + + + // so the i2c register is stored in the low byte of i2c_reg + // shift it 8 bits to left for sending as coefficient data (16 bits) + // hmm - why do I need a mask?? + // think either we mask here or in cs_8409_vendor_coef_set + // Apple is using short ints so likely automasked + i2c_reg_data = (i2c_reg << 8) & 0x0ffff; + + cs_8409_vendor_coef_set(codec, 0x5e, i2c_reg_data); + //if (retval == -1) + + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + rdcnt = -8; +sleep2: + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + if (retval != -1) + { + retval &= 0x18; + if (retval != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep2; + } + } + } + + // well thats interesting - looks as though the 16 bit return + // has the register in bits 15-8 and the data in 7-0 + // probably should mask the data out + retval = cs_8409_vendor_coef_get(codec, 0x5e); + //if (retval == -1) + + cs_8409_vendor_enableI2Cclock(codec, 0x0); + // exit on error + + myprintk_dbg("snd_hda_intel: i2cRead 0x%04x 0x%04x: 0x%04x end",i2c_address,i2c_reg,retval); + + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + // exit on error + + return retval; + +} + +static unsigned int cs_8409_vendor_i2cWrite(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int i2c_data, unsigned int paged) +{ + // AppleHDAFunctionGroupCS8409::_i2cWrite(bool, unsigned short, unsigned short, unsigned short) + unsigned int retval; + unsigned int i2c_reg_data; + int rdcnt; + + myprintk_dbg("snd_hda_intel: i2cWrite 0x%04x 0x%04x: 0x%04x %d",i2c_address,i2c_reg,i2c_data,paged); + + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + // exit on error + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); + // exit on error + + cs_8409_vendor_enableI2Cclock(codec, 0x1); + + + cs_8409_vendor_coef_set(codec, 0x59, i2c_address); + + if (paged) + { + unsigned int retval1; + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg >> 8); + + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + rdcnt = -8; +sleep1: + retval1 = cs_8409_vendor_coef_get(codec, 0x5c); + + if (retval1 != -1) + { + retval1 &= 0x18; + if (retval1 != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep1; + } + } + } + } + + + // so the i2c register is stored in the low byte of i2c_reg + // shift it 8 bits to left for sending as coefficient data (16 bits) + // then or in the 8 byte data + // mask here or in cs_8409_vendor_coef_set? + i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | ( i2c_data & 0x0ff); + + cs_8409_vendor_coef_set(codec, 0x5d, i2c_reg_data); + //if (retval == -1) + + + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + rdcnt = -8; +sleep2: + retval = cs_8409_vendor_coef_get(codec, 0x5c); + //if (retval == -1) + + if (retval != -1) + { + retval &= 0x18; + if (retval != 0x18) + { + if (rdcnt < 0) + { + rdcnt++; + // need 0x2 according to Apple + usleep_range(2000,4000); + goto sleep2; + } + } + } + + cs_8409_vendor_enableI2Cclock(codec, 0x0); + // exit on error + + myprintk_dbg("snd_hda_intel: i2cWrite 0x%04x 0x%04x: 0x%04x %d end",i2c_address,i2c_reg,i2c_data,paged); + + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + // exit on error + + return retval; +} + +static unsigned int cs_8409_vendor_i2cWriteMask(struct hda_codec *codec, unsigned int i2c_address, + unsigned int i2c_reg, unsigned int i2c_mask, unsigned int i2c_data, unsigned int paged) +{ + // masked version to emulate AppleHDATDMDevice::maskWriteReg(unsigned short, unsigned char, unsigned char) + + unsigned int retval; + unsigned int mask_val; + + retval = cs_8409_vendor_i2cRead(codec, i2c_address, i2c_reg, paged); + + mask_val = (retval & ~i2c_mask); + mask_val |= (i2c_data & i2c_mask); + + myprintk_dbg("snd_hda_intel: i2cWriteMask 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x) %d",i2c_address,i2c_reg,mask_val,retval,i2c_data,i2c_mask,paged); + + retval = cs_8409_vendor_i2cWrite(codec, i2c_address, i2c_reg, mask_val, paged); + + return retval; +} + + +// this seems to be how to do a list of verbs +// there is command to do a sequence of these +// snd_hda_sequence_write +static const struct hda_verb cs8409_init_verbs[] = { + //{0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ + //{0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + {} /* terminator */ +}; + +struct hda_coef { + u16 write; + hda_nid_t nid; + u32 idx; + u32 param; + u32 retdata; + int srcidx; +}; + +// new feature to do a sequence of coef read/writes +// (seems to be used a lot for cs8409) +// note that we ignore the return for gets for the moment!! +// ooh - new idea - save the logged return and check +static const struct hda_coef cs8409_init_coef[] = { + //{0, 0x01, idx, 0x00, retdata, 0}, read + //{1, 0x01, idx, param, dmydata, 0}, write + //{2, 0x01, idx, param, retdata, 0}, write mask +}; + +void snd_hda_coef_item(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 retdata, int srcidx) +{ + if (write_flag == 2) + { + // NOTA BENE - just for initial debugging differentiation - pass a mask of 0xffff for total overwrite + // use snd_hda_coef_item_masked for actual masked setup + unsigned int retreadval = cs_8409_vendor_coef_set_mask(codec, idx, param, 0xffff, 0, srcidx); + if (retreadval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD mask return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retreadval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD mask return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retreadval,retdata,nid,idx,param); + } + } + else if (write_flag == 1) + cs_8409_vendor_coef_set(codec, idx, param); + else + { + unsigned int retval = cs_8409_vendor_coef_get(codec, idx); + if (retval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retval,retdata,nid,idx,param); + } + } +} + +// just create a special routine if we wish to return the actual value for the moment +int snd_hda_coef_item_check(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 retdata, int srcidx) +{ + int retval = 0; + + if (write_flag == 2) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_check %d\n", write_flag); + else if (write_flag == 1) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_check %d\n", write_flag); + else + { + unsigned int retval1 = cs_8409_vendor_coef_get(codec, idx); + if (retval1 != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retval1,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retval1,retdata,nid,idx,param); + } + retval = retval1; + } + + return retval; +} + +void snd_hda_coef_item_masked(struct hda_codec *codec, u16 write_flag, hda_nid_t nid, u32 idx, u32 param, u32 mask, u32 retdata, u32 srcval, int srcidx) +{ + //int retval = 0; + if (write_flag != 2) + codec_dbg(codec, "command BAD usage of snd_hda_coef_item_masked %d\n", write_flag); + else + { + unsigned int retreadval = cs_8409_vendor_coef_set_mask(codec, idx, param, mask, srcval, srcidx); + if (retreadval != retdata) + { + if (srcidx > 0) + codec_dbg(codec, "command BAD mask return value at %d: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",srcidx,retreadval,retdata,nid,idx,param); + //else + // codec_dbg(codec, "command BAD mask return value: 0x%08x 0x%08x (0x%02x, 0x%04x, 0x%04x)\n",retreadval,retdata,nid,idx,param); + } + } + //return retval; +} + +void snd_hda_coef_sequence(struct hda_codec *codec, const struct hda_coef *seq, char *prtstr) +{ + mycodec_info(codec, "start snd_hda_coef_sequence %s\n",prtstr); + for (; seq->nid; seq++) + { + snd_hda_coef_item(codec, seq->write, seq->nid, seq->idx, seq->param, seq->retdata, seq->srcidx); + } + mycodec_info(codec, "end snd_hda_coef_sequence %s\n",prtstr); +} + +static inline unsigned int snd_hda_codec_read_check(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm, unsigned int check_val, int srcidx) +{ + unsigned int retval; + retval = snd_hda_codec_read(codec, nid, flags, verb, parm); + + if (retval == -1) + return retval; + + if (srcidx > 0) + if (retval != check_val) + codec_dbg(codec, "command BAD read check return value at %d: 0x%08x 0x%08x (0x%02x, 0x%03x 0x%04x)\n",srcidx,retval,check_val,nid,verb,parm); + + return retval; +} + +void snd_hda_double_reset(struct hda_codec *codec) +{ + mycodec_info(codec, "snd_hda_double_reset\n"); + // still not clear if this does anything + snd_hda_codec_write(codec, codec->core.afg, 0, 0xfff, 0); + // so far the double reset seems to give bad results - lots of registers dont compare + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_CODEC_RESET, 0); + msleep(1); + // apparently should use usleep_range for a few ms + //usleep_range(1000,2000); + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_CODEC_RESET, 0); +} + + +static void clear_pins(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + hda_nid_t nid; + + mycodec_info(codec, "start clear_pins\n"); + + for_each_hda_codec_node(nid, codec) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + /* use read here for syncing after issuing each verb */ + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + mycodec_info(codec, "end clear_pins\n"); +} + + +static void read_coefs_all_loop(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + int idx; + mycodec_info(codec, "start read_coefs_all\n"); + for (idx = 0; idx < 130; idx++) + { + int retval = cs_8409_vendor_coef_get(codec, idx); + mycodec_info(codec,"snd_hda_intel: read_coefs_all 0x%02x: 0x%08x\n",idx,retval); + } + mycodec_info(codec, "end read_coefs_all\n"); +} + +// this is very hacky but until get more understanding of what we can do with the 8409 setup +// re-define these from hda_codec.c here +// NOTA BENE - need to check this is consistent with any hda_codec.c updates!! + +/* + * audio-converter setup caches + */ +struct hda_cvt_setup { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; + +// we now setup our local cache data in the spec structure +// - cvt_setups is an opaque pointer type so we can see it here +// but we dont know how to access the data - except by re-defining hda_cvt_setup as above + +/* get or create a cache entry for the given audio converter NID */ +static struct hda_cvt_setup * +get_hda_cvt_setup_8409(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_cvt_setup *p; + int i; + + snd_array_for_each(&codec->cvt_setups, i, p) { + if (p->nid == nid) + return p; + } + p = snd_array_new(&codec->cvt_setups); + if (p) + p->nid = nid; + return p; +} + + +// so we actually need both versions - one using the hda_cvt_setup struct +// and one using our local hda_cvt_setup_apple struct + +static struct hda_cvt_setup_apple * +get_hda_cvt_setup_apple_8409(struct hda_codec *codec, hda_nid_t nid) +{ + struct cs8409_apple_spec *spec = codec->spec; + + switch (nid) + { + case 0x02: + return &spec->nid_0x02; + case 0x03: + return &spec->nid_0x03; + case 0x0a: + return &spec->nid_0x0a; + case 0x22: + return &spec->nid_0x22; + case 0x23: + return &spec->nid_0x23; + case 0x1a: + return &spec->nid_0x1a; + default: + break; + } + + codec_err(codec, "get_hda_cvt_setup_apple_8409: UNKNOWN NID!! 0x%02x\n", nid); + + return NULL; +} + +static void cs_8409_dump_stream_format(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_cvt_setup_apple *p = NULL; + int i; + + // use explicit search so we dont create one if doesnt exist + + for (i = 0; i < codec->cvt_setups.used; i++) { + p = snd_array_elem(&codec->cvt_setups, i); + if (p->nid == nid) + break; + } + + if (p != NULL) + mycodec_dbg(codec, "cs_8409_dump_stream_format: NID=0x%x, codec cached values: stream=0x%x, channel=%d, format=0x%x\n", nid, p->stream_tag, p->channel_id, p->format_id); + else + mycodec_dbg(codec, "cs_8409_dump_stream_format: NID=0x%x, codec cached values: NULL\n", nid); +} + +static void cs_8409_reset_stream_format(struct hda_codec *codec, hda_nid_t nid, int format, int doreset) +{ + // note that this routine is currently not used + + // this resets the cached stream format so that next + // stream setup will actually rewrite the stream format and stream id + // or if doreset set it will perform the stream update now + // also allow for only updating the stream format and not stream id + + // NOTE we now save the stream format in our local cache as the hda_codec cache + // is cleared at end of the prepare stage and we want to store it more permanently + // really only the stream id is variable + + struct hda_cvt_setup *p = NULL; + struct hda_cvt_setup_apple *papl = NULL; + u32 stream_tag_sv; + int channel_id_sv; + int format_id_sv; + + // problem - the get_hda_cvt_setup function is local to hda_codec - so need our own copy above + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + stream_tag_sv = papl->stream_tag; + channel_id_sv = papl->channel_id; + format_id_sv = papl->format_id; + + mycodec_info(codec, "cs_8409_reset_stream_format RESET for nid 0x%02x: 0x%08x id 0x%08x chan 0x%08x\n", nid, format_id_sv, stream_tag_sv, channel_id_sv); + + // snd_hda_codec_setup_stream uses a caching system so only sends verbs when a change occurs + // we want to force a send here so need to clear the cached data + + p = get_hda_cvt_setup_8409(codec, nid); + + p->stream_tag = 0; + p->channel_id = 0; + if (format) + p->format_id = 0; + + if (doreset) + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, channel_id_sv, format_id_sv); + +} + +// so what do I want this to do +// the stream format will be stored in the hda_cvt_setup (at what stage is this valid??) +// - we want to remove the Apple specific stream format/channel setup +// and just call snd_hda_setup_stream - but we need the actual stream format for this +// - hopefully getting from the hda_cvt_setup struct +// unfortunately this idea of storing in the hda_cvt_setup table turns out to be not useful +// as at end of snd_hda_codec_prepare it clears out (ie zeros) all unused/inactive cache entries +// so we have to store in a separate cache using our own copied definition for hda_cvt_setup +// hda_cvt_setup_apple + + +// the following 2 functions are used in the sync converter functions +// where apple essentially disables streaming (set stream id to 0) updates some vendor nid parameters +// then restores streaming +// so we store the stream info in a local variable copy and set it to the unused stream id ie stream id of 0 +// then cs_8409_update_from_save_stream_format sets it back to what it was +// note that the format is unchanged for these operations +// the main reason for doing it this way is because of the caching used in snd_hda_codec_setup_stream +// - if we just sent the hda verbs then the cached data in snd_hda_codec_setup_stream +// would be inconsistent with the actual state of streaming on the nid + +static void cs_8409_save_and_clear_stream_format(struct hda_codec *codec, hda_nid_t nid, struct hda_cvt_setup *savep) +{ + struct hda_cvt_setup *p = NULL; + u32 stream_tag_sv; + int channel_id_sv; + int format_id_sv; + + mycodec_dbg(codec, "cs_8409_save_and_clear_stream_format nid 0x%02x\n", nid); + + // use this to save the stream format and clear the stream id and channel + + p = get_hda_cvt_setup_8409(codec, nid); + + savep->stream_tag = p->stream_tag; + savep->channel_id = p->channel_id; + savep->format_id = p->format_id; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); +} + +static void cs_8409_update_from_save_stream_format(struct hda_codec *codec, hda_nid_t nid, struct hda_cvt_setup *savep, int update_stream_id, int update_format_id) +{ + struct hda_cvt_setup *p = NULL; + + mycodec_dbg(codec, "cs_8409_update_from_save_stream_format nid 0x%02x\n", nid); + + // so this will ensure the format is re-updated + + p = get_hda_cvt_setup_8409(codec, nid); + + if (update_stream_id) + { + p->stream_tag = 0; + p->channel_id = 0; + } + if (update_format_id) + p->format_id = 0; + + mycodec_info(codec, "cs_8409_update_from_save_stream_format tag 0x%08x chnl 0x%08x fmt 0x%08x\n", savep->stream_tag, savep->channel_id, savep->format_id); + + snd_hda_codec_setup_stream(codec, nid, savep->stream_tag, savep->channel_id, savep->format_id); +} + + +// so these are the crucial routines for setting our local cached copy of the stream info (in the spec structure) +// we use a different struct definition (hda_cvt_setup_apple) to keep the re-definition of hda_cvt_setup more local +// note that this stream info is only stored once per stream prepare function call +// and this routine always updates from that initial data + +static void cs_8409_really_update_stream_format(struct hda_codec *codec, hda_nid_t nid, int update_format_id, int update_stream_id, unsigned int new_channel_id) +{ + struct hda_cvt_setup *p = NULL; + struct hda_cvt_setup_apple *papl = NULL; + u32 stream_tag_sv = 0; + int channel_id_sv = 0; + int format_id_sv = 0; + + mycodec_dbg(codec, "cs_8409_really_update_stream_format nid 0x%02x updfmt %d updstrmid %d nchnlid %d\n", nid, update_format_id, update_stream_id, new_channel_id); + //dump_stack(); + + // so here we take our local cached format and save locally, clear out the cached values + // then call snd_hda_codec_setup_stream with the cached values + // this will ensure we update the HDA with the stream format + + // maybe now we should just update from our local stored version?? + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + if (papl != NULL) + { + stream_tag_sv = papl->stream_tag; + channel_id_sv = papl->channel_id; + format_id_sv = papl->format_id; + } + else + { + codec_err(codec, "cs_8409_really_update_stream_format bad nid 0x%02x FAIL!!\n", nid); + return; + } + + + p = get_hda_cvt_setup_8409(codec, nid); + + mycodec_info(codec, "cs_8409_really_update_stream_format cached tag 0x%08x chnl 0x%08x fmt 0x%08x\n", papl->stream_tag, papl->channel_id, papl->format_id); + + if (update_stream_id) + { + p->stream_tag = 0; + p->channel_id = 0; + } + if (update_format_id) + p->format_id = 0; + + mycodec_info(codec, "cs_8409_really_update_stream_format to update tag 0x%08x chnl 0x%08x fmt 0x%08x\n", p->stream_tag, p->channel_id, p->format_id); + + if (update_stream_id == 2) + mycodec_info(codec, "cs_8409_really_update_stream_format new tag 0x%08x chnl 0x%08x fmt 0x%08x\n", stream_tag_sv, new_channel_id, format_id_sv); + else + mycodec_info(codec, "cs_8409_really_update_stream_format new tag 0x%08x chnl 0x%08x fmt 0x%08x\n", stream_tag_sv, channel_id_sv, format_id_sv); + + cs_8409_dump_stream_format(codec, nid); + + if (update_stream_id == 2) + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, new_channel_id, format_id_sv); + else + snd_hda_codec_setup_stream(codec, nid, stream_tag_sv, channel_id_sv, format_id_sv); +} + +// remove function from compile so get error when building if use it +#if 0 +static void cs_8409_setup_stream_format(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) +{ + struct hda_cvt_setup *p = NULL; + + // NOTE - this function should no longer be used + + mycodec_dbg(codec, "cs_8409_setup_stream_format nid 0x%02x\n",nid); + + cs_8409_dump_stream_format(codec, nid); + + // this functions sets up the cached stream - get_hda_cvt_setup_8409 creates a struct if not yet defined + // NOTA BENE we do not do the update here - we are relying that this will be done by a call to + // cs_8409_really_update_stream_format now we have set the format correctly + + p = get_hda_cvt_setup_8409(codec, nid); + + // NOTA BENE - we do not set the channel id here - this will be done by cs_8409_really_update_stream_format + + p->stream_tag = stream_tag; + p->channel_id = 0; + p->format_id = format; + + cs_8409_dump_stream_format(codec, nid); + + mycodec_dbg(codec, "end cs_8409_setup_stream_format\n"); +} +#endif + +static void cs_8409_store_stream_format(struct hda_codec *codec, hda_nid_t nid, unsigned int stream_tag, unsigned int format) +{ + struct hda_cvt_setup_apple *papl = NULL; + + mycodec_dbg(codec, "cs_8409_store_stream_format nid 0x%02x\n",nid); + + cs_8409_dump_stream_format(codec, nid); + + // this functions sets up our local cached stream save store + // NOTA BENE we do not do the update here - we are relying that this will be done by a call to + // cs_8409_really_update_stream_format now we have set the format correctly + + papl = get_hda_cvt_setup_apple_8409(codec, nid); + + if (papl != NULL) + { + // NOTA BENE - we do not set the channel id here - this will be done by cs_8409_really_update_stream_format + + papl->stream_tag = stream_tag; + papl->channel_id = 0; + papl->format_id = format; + + mycodec_info(codec, "cs_8409_store_stream_format cached tag 0x%08x chnl 0x%08x fmt 0x%08x\n", papl->stream_tag, papl->channel_id, papl->format_id); + } + else + codec_err(codec, "cs_8409_store_stream_format bad nid 0x%02x FAIL!!\n", nid); + + mycodec_dbg(codec, "end cs_8409_store_stream_format\n"); +} + + +//int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); + +//static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) +// return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); + +// modified from init_input_src to switch the inputs on headset plugin/unplug events +static void switch_input_src(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + struct nid_path *path; + int i, c, nums; + mycodec_dbg(codec, "switch_input_src enter\n"); + + nums = spec->num_adc_nids; + + mycodec_dbg(codec, "switch_input_src num adc nids %d %d\n",nums,spec->dyn_adc_switch); + + for (c = 0; c < nums; c++) { + mycodec_dbg(codec, "switch_input_src num_items %d\n",imux->num_items); + for (i = 0; i < imux->num_items; i++) { + //path = get_input_path(codec, c, i); + path = snd_hda_get_path_from_idx(codec, spec->input_paths[i][c]); + if (path) { + int in; + bool active = path->active; + mycodec_dbg(codec, "switch_input_src path active %d\n",active); + for (in = path->depth - 1; in >= 0; in--) { + hda_nid_t tnid = path->path[in]; + mycodec_dbg(codec, "switch_input_src path nid %d: 0x%02x\n",in,tnid); + } + if (path->active) { + mycodec_dbg(codec, "switch_input_src path nid 0x%02x deactivate\n",path->path[1]); + snd_hda_activate_path(codec, path, false, false); + } else { + mycodec_dbg(codec, "switch_input_src path nid 0x%02x activate\n",path->path[1]); + snd_hda_activate_path(codec, path, true, false); + } + } + else { + mycodec_dbg(codec, "switch_input_src path NULL\n"); + } + } + } + + mycodec_dbg(codec, "switch_input_src exit\n"); +} + + + + +static int read_gpio_status_check(struct hda_codec *codec); + +#ifdef USE_DATA + +#include "patch_cirrus_data84.h" + +#include "patch_cirrus_plugin.h" + +#include "patch_cirrus_headplay.h" + +#include "patch_cirrus_unplug.h" + +#include "patch_cirrus_plugin3.h" + +#include "patch_cirrus_plugin23.h" + +#include "patch_cirrus_mb141_data84.h" + +#else + +// error definitions +static void cs_8409_external_device_unsolicited_response_data(struct hda_codec *codec, unsigned int res) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_boot_setup_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_play_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_play_real(struct hda_codec *codec); + +static void cs_8409_playstop_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_playstop_real(struct hda_codec *codec); + +static void cs_8409_headplay_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_headplay_real(struct hda_codec *codec); + +static void cs_8409_headplaystop_data(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +//static void cs_8409_headplaystop_real(struct hda_codec *codec); + + +static void cs_8409_boot_setup_data_ssm3(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + +static void cs_8409_play_data_ssm3(struct hda_codec *codec) +{ + dev_err(hda_codec_dev(codec), "ERROR - to use data functions need to define USE_DATA\n"); +} + + +#endif + +#include "patch_cirrus_boot84.h" + +#include "patch_cirrus_real84_i2c.h" + +#include "patch_cirrus_real84.h" + +// only needed if wish to test the version using the mb141 logs +// cs_8409_boot_setup_real now supposed to do both machines +//#include "patch_cirrus_mb141_real84.h" + + +// macbook pro subsystem ids +// 14,1 0x106b3300 +// 14,2 0x106b3600 +// 14,3 0x106b3900 + +// imac subsystem ids +// 18,1 0x106b0e00 +// 18,2 0x106b0f00 +// 18,3 0x106b1000 +// 19,1 0x106b1000 + + +static int cs_8409_data_config(struct hda_codec *codec); +static int cs_8409_real_config(struct hda_codec *codec); + + +static int cs_8409_boot_setup(struct hda_codec *codec) +{ + int err = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // so it appears we break up the subsystem_id into 2 parts + // a codec vendor id (16 bits) and a subvendor id (8 bits) plus an assembly id + // so here the codec vendor is 0x106b, the subvendor id is 0x39 and the assembly id is 0x00 + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_data_config\n"); + + err = cs_8409_data_config(codec); + + myprintk("snd_hda_intel: cs_8409_boot_setup post cs_8409_data_config\n"); + } else { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_real_config\n"); + + err = cs_8409_real_config(codec); + + myprintk("snd_hda_intel: cs_8409_boot_setup post cs_8409_real_config\n"); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + cs_8409_boot_setup_data_ssm3(codec); + } else { + myprintk("snd_hda_intel: cs_8409_boot_setup pre cs_8409_real_config\n"); + //cs_8409_boot_setup_real_ssm3(codec); + err = cs_8409_real_config(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_boot_setup pre data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + err = cs_8409_real_config(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + err = -1; + } + + return err; +} + +void cs_8409_play_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + myprintk_dbg("snd_hda_intel: cs_8409_play_setup\n"); + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + //cs_8409_unmute_data(codec); + //cs_8409_volup_data(codec); + cs_8409_play_data(codec); + } else { + cs_8409_play_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_play_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_play_setup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_play_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +//static void cs_8409_playstop_data_ssm3(struct hda_codec *codec); +//static void cs_8409_playstop_real_ssm3(struct hda_codec *codec); + +void cs_8409_play_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + myprintk_dbg("snd_hda_intel: cs_8409_play_cleanup\n"); + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_playstop_data(codec); + } else { + cs_8409_playstop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_playstop_data_ssm3(codec); + } else { + //cs_8409_playstop_real_ssm3(codec); + cs_8409_playstop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_play_cleanup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_playstop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + +} + + +// NOTE - so far all systems use the same inputs for internal mike capturing - not sure if +// there are any subsystem_id differences + +void cs_8409_capture_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capture_data(codec); + } else { + //cs_8409_capture_real(codec); + cs_8409_capture_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + + +void cs_8409_capture_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capturestop_data(codec); + } else { + //cs_8409_capturestop_real(codec); + cs_8409_capturestop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + +} + + +static void cs_8409_cs42l83_unsolicited_response_finalize(struct hda_codec *codec, unsigned int res); + +static void cs_8409_perform_external_device_unsolicited_responses(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + struct unsol_item *unsol_entry = NULL; + struct unsol_item *unsol_temp = NULL; + if (!list_empty(&spec->unsol_list)) { + codec_info(codec, "cs_8409_perform_external_device_unsolicited_responses UNSOL start\n"); + list_for_each_entry_safe(unsol_entry, unsol_temp, &spec->unsol_list, list) + { + list_del_init(&unsol_entry->list); + // pigs this gets complicated - these might issue other unsol responses + cs_8409_cs42l83_unsolicited_response_finalize(codec, unsol_entry->res); + spec->unsol_items_prealloc_used[unsol_entry->idx] = 0; + memset(unsol_entry, 0, sizeof(struct unsol_item)); + } + codec_info(codec, "cs_8409_perform_external_device_unsolicited_responses UNSOL end\n"); + } +} + +static void cs_8409_cs42l83_unsolicited_response(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + // not clear if want to use the GPIO pins apparently passed in res to determine + // if want to do interrupt checking here and if no interrupts then to do + // some other unsolicited response function (not seen any such unsolicited responses yet) + // without checking the unsolicited block status + + // now think dont need a list - we can only have 1 outstanding unsolicted interrupt request + // we may get multiple unsolicited interrupt requests - but they all will have same GPIO status (0x26) + // and we determine the exact interrupt by reading the cs42l83 registers - which we are trying to avoid + // clashing with other verbs + // it may be that we get multiple interrupt flags to handle when we do read - not seen so far + + if (spec->block_unsol) + { + int itm; + int new_itm = -1; + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - UNSOL BLOCKED\n"); + for (itm=0; itm<10; itm++) + if (spec->unsol_items_prealloc_used[itm] == 0) { new_itm = itm; break; } + if (new_itm < 0) + { + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - IGNORING UNSOL RESPONSE!!\n"); + return; + } + spec->unsol_items_prealloc_used[new_itm] = 1; + memset(&(spec->unsol_items_prealloc[new_itm]), 0, sizeof(struct unsol_item)); + spec->unsol_items_prealloc[new_itm].res = res; + spec->unsol_items_prealloc[new_itm].idx = new_itm; + list_add_tail(&(spec->unsol_items_prealloc[new_itm].list), &spec->unsol_list); + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - UNSOL response stored\n"); + return; + } + else + codec_info(codec, "cs_8409_cs42l83_unsolicited_response - NOT UNSOL BLOCKED\n"); + + // so it appears we need to block unsol responses while doing unsol responses + // this is probably not the way to do this but still havent figured out how to use locking properly + // as this needs to be interruptible because some of these functions take a long time + // I think if we get here we cannot have been blocked so list maybe always empty + // whats not clear is if list_for_each_entry_safe is safe for addition also + spec->block_unsol = 1; + + cs_8409_cs42l83_unsolicited_response_finalize(codec, res); + + if (!list_empty(&spec->unsol_list)) + { + mycodec_info(codec, "cs_8409_cs42l83_unsolicited_response - performing blocked responses start\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + mycodec_info(codec, "cs_8409_cs42l83_unsolicited_response - performing blocked responses end\n"); + } + + spec->block_unsol = 0; +} + +static void cs_8409_cs42l83_unsolicited_response_finalize(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (spec->use_data) + cs_8409_external_device_unsolicited_response_data(codec, res); + else + { + if (spec->headset_phase == 0) + { + mycodec_info(codec, "cs_8409_external_device_unsolicited_response_finalize - phase is 0 - skipping\n"); + return; + } + + + // note the data version will only play thro the headphones for a single time + //cs_8409_external_device_unsolicited_response_data(codec, res); + cs_8409_external_device_unsolicited_response(codec); + } +} + + +static void cs_8409_headset_mike_setup_nouse(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_intmike_linein_disable(codec); + + cs_8409_headset_mike_streaming_preplay(codec, 1); + cs_8409_headset_mike_buttons_enable(codec); +} + + +void cs_8409_headplay_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_headplay_data(codec); + } else { + cs_8409_headplay_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_headplay_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_headplay_setup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_headplay_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream setup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headplay_setup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +void cs_8409_headplay_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3900) { + if (spec->use_data) { + cs_8409_headplaystop_data(codec); + } else { + //cs_8409_headplaystop_data(codec); + cs_8409_headplaystop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + if (spec->use_data) { + //cs_8409_play_data_ssm3(codec); + } else { + //cs_8409_play_real_ssm3(codec); + cs_8409_headplaystop_real(codec); + } + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + printk("snd_hda_intel: cs_8409_headplay_cleanup data not implemented for subsystem id 0x%08x",codec->core.subsystem_id); + } else { + cs_8409_headplaystop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream cleanup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headplay_cleanup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +// NOTE - so far all systems use the same chip (cs42l83) for headset mike capturing - not sure if +// there are any subsystem_id differences + +void cs_8409_headcapture_setup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_headcapture_data(codec); + } else { + cs_8409_headcapture_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream setup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headcapture_setup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +void cs_8409_headcapture_cleanup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600 || codec->core.subsystem_id == 0x106b3900 + || codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + if (spec->use_data) { + //cs_8409_capturestop_data(codec); + } else { + //cs_8409_capturestop_real(codec); + cs_8409_headcapturestop_real(codec); + } + } + else { + printk("snd_hda_intel: UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // decided this needs moving till all stream cleanup verbs done + //spec->block_unsol = 0; + + //if (!list_empty(&spec->unsol_list)) + //{ + // codec_info(codec, "cs_8409_headcapturestop_cleanup - performing UNSOL responses\n"); + // cs_8409_perform_external_device_unsolicited_responses(codec); + //} +} + + +static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (action == HDA_GEN_PCM_ACT_PREPARE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 curtim; + ktime_get_real_ts64(&curtim); +#else + struct timespec curtim; + getnstimeofday(&curtim); +#endif + myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec); + if (1) { + struct hda_cvt_setup_apple *p = NULL; + //int power_chk = 0; + + // in the new way we set the stream up here using the passed data + // - this does not actually update the stream format here but sets the cached parameters + // so the cs_8409_really_update_stream_format will cause the updates to occur + // note we explicitly set the channel id - dont see another way yet + + //cs_8409_setup_stream_format(codec, 0x02, stream_tag, format); + cs_8409_store_stream_format(codec, 0x02, stream_tag, format); + + //cs_8409_setup_stream_format(codec, 0x03, stream_tag, format); + cs_8409_store_stream_format(codec, 0x03, stream_tag, format); + + //cs_8409_setup_stream_format(codec, 0x0a, stream_tag, format); + cs_8409_store_stream_format(codec, 0x0a, stream_tag, format); + + // save number of actual stream channels + spec->stream_channels = substream->runtime->channels; + + hda_check_power_state(codec, 0x1a, 1); + hda_check_power_state(codec, 0x3c, 1); + + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + // we need to split this to deal with capture only setup + // and capture with play setup + // note this does mean we setup the mike in a different order to OSX + // if we are capturing with playing - because the capture setup seems to be done + // first on Linux and we dont know at that stage if we will be playing + if (spec->have_mike) + { + // actually we always need to do cs_8409_headplay_setup - here we are about to play + // - what this possibly would allow is the apple way of doing a pre-setup + // so here we would switch between doing a full setup or a partial setup + if (spec->headset_play_format_setup_needed) + { + cs_8409_headplay_setup(codec); + spec->headset_play_format_setup_needed = 0; + } + // we only setup capturing if we are actually doing capturing + //if (spec->headset_capture_format_setup_needed) + //{ + // cs_8409_headcapture_setup(codec); + // spec->headset_capture_format_setup_needed = 0; + //} + } + else + { + cs_8409_headplay_setup(codec); + } + } + else { + cs_8409_play_setup(codec); + } + myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook setup play called"); + + hda_check_power_state(codec, 0x1a, 2); + hda_check_power_state(codec, 0x3c, 2); + + + // I dont now understand how this worked - the codes above ALWAYS reset the stream format + // to the OSX format + // and unless I force a stream update here there will be a stream format difference + // yet it appears it worked - even tho sometimes there was no format update after this routine + // now I dont know why + + + // so we need to force the stream to be re-set here + // problem is it appears hda_codec caches the stream format and id and only updates if changed + // and there doesnt seem to be a good way to force an update + + // this routine doesnt seem to be nid specific - so explicitly fix the known nids here + // no longer needed now we set the stream format correctly above + // so when snd_hda_multi_out_analog_prepare is called after this routine it should do nothing + // as we will have cached and set the right format now + + //cs_8409_reset_stream_format(codec, 0x02, 1, 0); + + //cs_8409_reset_stream_format(codec, 0x03, 1, 0); + + //cs_8409_reset_stream_format(codec, 0x0a, 1, 0); + + + spec->playing = 0; + + + spec->play_init = 1; + } + } +} + + +static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) +{ + + struct cs8409_apple_spec *spec = codec->spec; + + // so finally getting a handle on ordering here + // we need to do the OSX setup in the OPEN section + // as the generic hda format and stream setup is done BEFORE the PREPARE hook + // (theres a good chance we only need to do this once at least as long as machine doesnt sleep) + // (or we could just override the prepare function completely) + // I now think the noise was caused by mis-match between the stream format and the nid setup format + // (because the generic setup was done before the OSX setup and the actual streamed format is slightly different) + // (the hda documentation says these really need to match) + // It appears the 8409 setup can handle at least some differences in the stream format + // as long as we set the nid to format the kernel is sending + // certainly seems to handle S24_LE or S32_LE differences (OSX format is always S24_3LE) + + + if (action == HDA_GEN_PCM_ACT_OPEN) { + //struct hda_cvt_setup_apple *p = NULL; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open"); + + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open end"); + } else if (action == HDA_GEN_PCM_ACT_PREPARE) { + // so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + struct timespec64 curtim; + ktime_get_real_ts64(&curtim); +#else + struct timespec curtim; + getnstimeofday(&curtim); +#endif + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec); + //int power_chk = 0; + //power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + //myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 2 %d", power_chk); + // this is where we need to finally unset the block_unsol + // - which also means this is where we should check for unsolicited responses + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + spec->playing = 1; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE end"); + } else if (action == HDA_GEN_PCM_ACT_CLEANUP) { + // so this also comes AFTER the stream format, frequency cleanup verbs are sent for the pcm stream + int power_chk = 0; + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK CLEANUP"); + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 3 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook jack_present %d\n",spec->jack_present); + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + // so dont think need to anything about capturing here + cs_8409_headplay_cleanup(codec); + spec->headset_play_format_setup_needed = 1; + } + else + cs_8409_play_cleanup(codec); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook done play down"); + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + // not sure of this position yet + spec->playing = 0; + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 4 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK CLEANUP end"); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook close"); + myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook close end"); + } + +} + + +static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream, + int action) +{ + struct cs8409_apple_spec *spec = codec->spec; + + if (action == HDA_GEN_PCM_ACT_PREPARE) { + myprintk("snd_hda_intel: command cs_8409_pcm_capture_pre_prepare_hook HOOK PREPARE init %d",spec->capture_init); + + // so the first action for internal mike recording (via Quicktime) + // is a headphone sense + // followed by amp setup for playing - is this just a feature of Quicktime?? + // maybe Quicktime just auto sets up play just in case + // we dont seem to have a headphone sense if we have already plugged in the headset + // not that we can do anything - except abort if no headset plugged in?? + + //if (hinfo->nid == 0x22) + //{ + // int retval; + // retval = cs42l83_headphone_sense(codec); + //} + + + // I think this is the same for intmike or headset mike + //cs_8409_setup_stream_format(codec, hinfo->nid, stream_tag, format); + cs_8409_store_stream_format(codec, hinfo->nid, stream_tag, format); + + + // for the moment have junky test here + if (spec->jack_present) { + spec->block_unsol = 1; + if (spec->have_mike) + { + // so it seems if we have a headset mike we always enable the + // headphones even if just capturing + if (spec->headset_play_format_setup_needed) + { + cs_8409_headplay_setup(codec); + spec->headset_play_format_setup_needed = 0; + } + if (spec->headset_capture_format_setup_needed) + { + cs_8409_headcapture_setup(codec); + spec->headset_capture_format_setup_needed = 0; + } + } + // I think this is impossible - this would say we tried to capture + // using a headset without mike + // NOTE - still not fixed linein/lineout working - this may need + // changing here + } + else + cs_8409_capture_setup(codec); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook setup capture called"); + + spec->capturing = 0; + + + spec->capture_init = 1; + } +} + +static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, int action) +{ + + //struct cs8409_apple_spec *spec = codec->spec; + + struct cs8409_apple_spec *spec = NULL; + + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init called"); + + //dump_stack(); + + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init post stack"); + + // - so this seems to be the critical issue - this can apparently be called with a NULL codec!!! + // only thing to do seems to be to return!! + if (codec == NULL) { + struct hda_codec *badptr = NULL; + printk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NULL"); + // so if we are here it looks as tho we have been called from call_hp_automute + // - in which the codec is the 1st arg + badptr = (struct hda_codec *) hinfo; + spec = badptr->spec; + printk("snd_hda_intel: cs_8409_capture_pcm_hook - pcm_playback_hook %p", spec->gen.pcm_playback_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - pcm_capture_hook %p", spec->gen.pcm_capture_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - hp_automute_hook %p", spec->gen.hp_automute_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - line_automute_hook %p", spec->gen.line_automute_hook); + printk("snd_hda_intel: cs_8409_capture_pcm_hook - line_automute_hook %p", spec->gen.mic_autoswitch_hook); + printk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NULL exit"); + return; + } + else + myprintk_dbg("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK init - CODEC NOT NULL"); + + //dump_stack(); + + spec = codec->spec; + + + // so now no setup is done here - we only check for unsolicited responses + // - we do do cleanup for the CLEANUP action + + if (action == HDA_GEN_PCM_ACT_OPEN) { + //struct hda_cvt_setup_apple *p = NULL; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open"); + + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open end"); + } else if (action == HDA_GEN_PCM_ACT_PREPARE) { + // so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK PREPARE init %d",spec->capture_init); + // this is where we need to finally unset the block_unsol + // - which also means this is where we should check for unsolicited responses + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_capture_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + spec->capturing = 1; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK PREPARE end"); + } else if (action == HDA_GEN_PCM_ACT_CLEANUP) { + // so this also comes AFTER the stream format, frequency cleanup verbs are sent for the pcm stream + int power_chk = 0; + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK CLEANUP"); + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook power check 0x01 3 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook jack_present %d\n",spec->jack_present); + // for the moment have junky test here + if (spec->jack_present) + { + // for the moment I think this works for both MB 14,1 and 14,3 - same hda and headphone chip + // note that so far only the headphone chip seems to generate unsol responses usually + spec->block_unsol = 1; + if (spec->headset_capture_format_setup_needed == 0) + { + cs_8409_headcapture_cleanup(codec); + spec->headset_capture_format_setup_needed = 1; + } + if (!spec->playing) + { + if (spec->headset_play_format_setup_needed == 0) + { + cs_8409_headplay_cleanup(codec); + spec->headset_play_format_setup_needed = 1; + } + } + } + else + cs_8409_capture_cleanup(codec); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook done capture down"); + spec->block_unsol = 0; + if (!list_empty(&spec->unsol_list)) + { + codec_info(codec, "cs_8409_capture_pcm_hook - performing UNSOL responses\n"); + cs_8409_perform_external_device_unsolicited_responses(codec); + } + // not sure of this position yet + spec->capturing = 0; + power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook power check 0x01 4 %d", power_chk); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK CLEANUP end"); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook close"); + myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook close end"); + } + +} + + +// this version runs all explicit commands as logged on OSX +static int cs_8409_data_config(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + //hda_nid_t beep_nid = spec->beep_nid; + + unsigned int tmpstate1 = -1; + unsigned int tmpstate2 = -1; + unsigned int tmpstate3 = -1; + unsigned int tmpstate4 = -1; + + myprintk("snd_hda_intel: cs8409_data_config"); + + + cs_8409_boot_setup_data(codec); + + + // check what power state of these nodes is - Apple does not do this + tmpstate1 = hda_sync_power_state_8409(codec, 0x48, AC_PWRST_D0); + tmpstate2 = hda_sync_power_state_8409(codec, 0x49, AC_PWRST_D0); + tmpstate3 = hda_sync_power_state_8409(codec, 0x4a, AC_PWRST_D0); + tmpstate4 = hda_sync_power_state_8409(codec, 0x4b, AC_PWRST_D0); + + myprintk("snd_hda_intel: cs8409_data_config power 0x48 %d 0x49 %d 0x4a %d 0x4b %d\n",tmpstate1,tmpstate2,tmpstate3,tmpstate4); + + + myprintk("snd_hda_intel: cs8409_data_config end"); + + return 0; +} + + +// this version runs the setup using functions based on the setup using the logged data +static int cs_8409_real_config(struct hda_codec *codec) +{ + //struct cs8409_apple_spec *spec = codec->spec; + //hda_nid_t beep_nid = spec->beep_nid; + + unsigned int tmpstate1 = -1; + unsigned int tmpstate2 = -1; + unsigned int tmpstate3 = -1; + unsigned int tmpstate4 = -1; + + myprintk("snd_hda_intel: cs8409_real_config"); + + + cs_8409_boot_setup_real(codec); + + + // check what power state of these nodes is - Apple does not do this + tmpstate1 = hda_sync_power_state_8409(codec, 0x48, AC_PWRST_D0); + tmpstate2 = hda_sync_power_state_8409(codec, 0x49, AC_PWRST_D0); + tmpstate3 = hda_sync_power_state_8409(codec, 0x4a, AC_PWRST_D0); + tmpstate4 = hda_sync_power_state_8409(codec, 0x4b, AC_PWRST_D0); + + myprintk("snd_hda_intel: cs8409_real_config power 0x48 %d 0x49 %d 0x4a %d 0x4b %d\n",tmpstate1,tmpstate2,tmpstate3,tmpstate4); + + + myprintk("snd_hda_intel: cs8409_real_config end"); + + return 0; +} + diff --git a/patch_cirrus_real84.h b/patch_cirrus_real84.h new file mode 100644 index 0000000..1a1cd04 --- /dev/null +++ b/patch_cirrus_real84.h @@ -0,0 +1,5954 @@ + +static int tdm_in_use(struct hda_codec *codec, int where_flag) +{ + int coef_ret = 0; + int coef_idx = 0; + + // re-implementation of AppleHDATDMBusManagerCS8409::tdmInUse + mycodec_info(codec, "command tdmInUse start %d\n", where_flag); + + // note on OSX the coef get functions returns a status value with read value stored in passed address + // on linux it seems -1 is an error return + + coef_ret = cs_8409_vendor_coef_get(codec, 0x19); + + //if (coef_ret == -1) error; + + coef_idx = 0x1a; + + do { + + if ((short)coef_ret >= 0) { + mycodec_info(codec, "command tdmInUse 1 end %d\n", where_flag); + return 1; + } + + coef_ret = cs_8409_vendor_coef_get(codec, coef_idx); + + //if (coef_ret == -1) error; + + coef_idx++; + + } while (coef_idx <= 0x57); + + mycodec_info(codec, "command tdmInUse 0 end %d\n", where_flag); + + return 0; + +} + + +static int cs42l83_headphone_sense(struct hda_codec *codec) +{ + int retval = 0; + + // AppleHDATDM_Codec::getHeadphonePinSense(bool*, unsigned int*) + + // register 0x1b77 - Detect Status 1 + // value 0x96 0x80 HP plugged bias 0x16 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7700 i2c data 0x7796 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b77, 1); // snd_hda + + mycodec_info(codec, "cs42l83_headphone_sense - 0x%04x\n", retval); + + return retval; +} + +static int read_gpio_status_check(struct hda_codec *codec) +{ + int retval; + + // should these be done powered down?? + // lets check power state here + + retval = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0x00000000); // 0x001f1500 + + mycodec_info(codec, "command read_gpio_status_check %x\n", retval); + + return retval; +} + + +static void cs_8409_intmike_format_setup_format33_nouse(struct hda_codec *codec) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 +// snd_hda: stream format 34 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + +} + +static void cs_8409_intmike_format_setup_format_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x02224031 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x02224031 +// snd_hda: stream format 34 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + +} + +static void cs_8409_intmike_format_setup_enable(struct hda_codec *codec, int hda_format, int powered_down) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + // now updated to not write the Apple format but use my format setting routines + // (remember we have limited the allowed formats to acceptable ones) + // note that apparently we can set the format with the nid powered down but for setting the + // stream id the nid has to be powered up + // this seems to be used a lot in plugin/unplug headset in a powered down state + // - but when capturing no power changes done + + // for some very strange reason we setup a 4 channel format after unplug of headset with mike + // - otherwise its 2 channel - pass the format to allow for this + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02224033 +// snd_hda: stream format 34 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, hda_format); // 0x02224033 + + + // now assuming have saved the stream info prior to calling this function + + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10515); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10518); // 0x022f0500 + //if (powered_down) hda_set_node_power_state(codec, 0x22, AC_PWRST_D0); + if (powered_down) hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 1)] + + + // using the stored stream parameters update nid 0x22 stream parameters + // we have limited the allowed formats so should only have working formats here + //cs_8409_really_update_stream_format(codec, 0x22, 1, 1, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 1, 0); + + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10521); // 0x022f0500 + //if (powered_down) hda_set_node_power_state(codec, 0x22, AC_PWRST_D3); + if (powered_down) hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + +// snd_hda: # AppleHDAWidgetCS8409::setConnectionSelect: + ret_coef9 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x000000b3, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef read 10523 + //new_coef9 = (ret_coef9 | 0x20); // note most of the time it just seems to copy the value because bit 0x20 already set on input + // // only on boot does this get set + new_coef9 = (ret_coef9 | spec->reg9_intmike_dmic_mo); // note most of the time it just seems to copy the value because bit 0x20 already set on input + // only on boot does this get set + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_format_setup_enable coef 0x09 update 0x%04x 0x%04x \n", ret_coef9, new_coef9); + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0x00000000, 10527 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10527 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, new_coef9, 0x00000000, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10527 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02270100 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02270100 + +} + + +static void cs_8409_volume_set(struct hda_codec *codec, hda_nid_t nid, int volume) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236027 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_volume_mute(struct hda_codec *codec, hda_nid_t nid) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236000 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_volume_unmute(struct hda_codec *codec, hda_nid_t nid) +{ + int retgain; + int newgain; + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000); // 0x022b2000 + + newgain = (retgain & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236000 + + retgain = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000); // 0x022b0000 + + newgain = (retgain & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + +} + +static void cs_8409_intmike_volume_set(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_set(codec, spec->intmike_adc_nid, volume); +} + +static void cs_8409_linein_volume_set(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_set(codec, spec->linein_amp_nid, volume); +} + +static void cs_8409_intmike_volume_unmute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_unmute(codec, spec->intmike_adc_nid); +} + +static void cs_8409_linein_volume_unmute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_unmute(codec, spec->linein_amp_nid); +} + +static void cs_8409_intmike_volume_mute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_mute(codec, spec->intmike_adc_nid); +} + +static void cs_8409_linein_volume_mute(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + cs_8409_volume_mute(codec, spec->linein_amp_nid); +} + +static void cs_8409_intmike_volume_setup_new(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_volume_set(codec, spec->intmike_adc_nid, volume); + + cs_8409_volume_mute(codec, spec->intmike_adc_nid); + + cs_8409_volume_set(codec, spec->intmike_nid, 0x00); + +} + +static void cs_8409_linein_volume_setup_new(struct hda_codec *codec, int volume) +{ + struct cs8409_apple_spec *spec = codec->spec; + + cs_8409_volume_set(codec, spec->linein_amp_nid, volume); + + cs_8409_volume_mute(codec, spec->linein_amp_nid); + + cs_8409_volume_set(codec, spec->linein_nid, 0x00); + +} + + +static void cs_8409_intmike_volume_setup(struct hda_codec *codec, int volume) +{ + int retgain; + int newgain; + + struct cs8409_apple_spec *spec = codec->spec; + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000033, 0); // 0x022b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000033, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x0033 mute 0 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02236027 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006027); // 0x02236027 +// snd_hda: amp gain/mute 34 0x6027 mute 0 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000033, 0); // 0x022b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000033, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x0033 mute 0 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x02235027 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005027); // 0x02235027 +// snd_hda: amp gain/mute 34 0x5027 mute 0 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + + // mute + // plausibly AppleHDAWidget::setWidgetAmplifierMute + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000027, 0); // 0x022b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000027, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x0027 mute 0 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x022360a7 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x022360a7 +// snd_hda: amp gain/mute 34 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000027, 0); // 0x022b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000027, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x0027 mute 0 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x022350a7 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x022350a7 +// snd_hda: amp gain/mute 34 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + + // this is working on node 0x44 macbook pro + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x044b2000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x044b2000 +// snd_hda: amp gain/mute 68 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 68 0x0000 mute 0 gain 0x0 0 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x04436000 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04436000 +// snd_hda: amp gain/mute 68 0x6000 mute 0 gain 0x0 0 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x044b0000 + retgain = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x044b0000 +// snd_hda: amp gain/mute 68 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 68 0x0000 mute 0 gain 0x0 0 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x04435000 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04435000 +// snd_hda: amp gain/mute 68 0x5000 mute 0 gain 0x0 0 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_setup(struct hda_codec *codec, int volume) +{ + int retgain; + int newgain; + + struct cs8409_apple_spec *spec = codec->spec; + + // so as far as I can see the 1st section sets the gain and the second section sets the mute + // it appears we do masked updates + + //volume = 0x27; + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x6000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 +// snd_hda: amp gain/mute 35 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + + newgain = (retgain & 0x80) | (volume & 0x7f) | 0x5000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023350a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x023350a7 +// snd_hda: amp gain/mute 35 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + // mute + // plausibly AppleHDAWidget::setWidgetAmplifierMute + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x6000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023360a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060a7); // 0x023360a7 +// snd_hda: amp gain/mute 35 0x60a7 mute 1 gain 0x27 39 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + + newgain = (retgain & 0x7f) | 0x80 | 0x5000; + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, newgain); // 0x023350a7 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050a7); // 0x023350a7 +// snd_hda: amp gain/mute 35 0x50a7 mute 1 gain 0x27 39 index 0x00 left 0 right 1 output 0 input 1 right input + + // this is working on node 0x45 macbook pro + + // plausibly AppleHDAWidget::setWidgetAmplifierGain + + //retgain = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x045b2000 + retgain = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x00000000, 0); // 0x045b2000 +// snd_hda: amp gain/mute 69 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 69 0x0000 mute 0 gain 0x0 0 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04536000 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006000); // 0x04536000 +// snd_hda: amp gain/mute 69 0x6000 mute 0 gain 0x0 0 index 0x00 left 1 right 0 output 0 input 1 left input + + //retgain = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x045b0000 + retgain = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x00000000, 0); // 0x045b0000 +// snd_hda: amp gain/mute 69 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 69 0x0000 mute 0 gain 0x0 0 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04535000 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005000); // 0x04535000 +// snd_hda: amp gain/mute 69 0x5000 mute 0 gain 0x0 0 index 0x00 left 0 right 1 output 0 input 1 right input + +} + + +static void cs_8409_intmike_stream_on_nid(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00005400, 0 ); // coef read 10544 + + //new_coef82 = (reg_coef82 | 0x1); + new_coef82 = (reg_coef82 | spec->reg82_intmike_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_stream_on_nid coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x5401, 0x00000000, 10548 ); // coef write 10548 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 10548 + + //retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x044f0700 + retval = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x044f0700 + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04470720 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04470720 +// snd_hda: 68 ['AC_PINCTL_IN_EN'] + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + +} + + +static void cs_8409_intmike_format_setup_disable(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + + // set to defaults and disable input + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12217); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12220); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12223); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02220000 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02220000 +// snd_hda: stream format 34 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + // AppleHDAWidgetCS8409::configurePinForIO(bool)?? + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000a801, 0 ); // coef read 12226 + + //new_coef82 = (reg_coef82 & 0xfffffffe); + new_coef82 = (reg_coef82 & ~spec->reg82_intmike_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_intmike_format_setup_disable coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x00000000, 12230 ); // coef write 12230 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 12230 + + //retval = snd_hda_codec_read_check(codec, 0x44, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x044f0700 + retval = snd_hda_codec_read_check(codec, spec->intmike_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x044f0700 + + snd_hda_codec_write(codec, spec->intmike_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 + + //snd_hda_codec_write(codec, 0x44, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04470700 +// snd_hda: 68 [] + +} + + +static void cs_8409_linein_format_setup_disable(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // 0x45 -> 0x23 is line input (macbook pro) + + + // set to defaults and disable input + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12248); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12251); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12254); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02320000 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x02320000 +// snd_hda: stream format 35 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x0000a800, 0 ); // coef read 12257 + + //new_coef82 = (reg_coef82 & 0xfffffffd); + new_coef82 = (reg_coef82 & ~spec->reg82_linein_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_format_setup_disable coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x00000000, 12261 ); // coef write 12261 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 0 ); // coef write 12261 + + //retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + retval = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x04570700 +// snd_hda: 69 [] + +} + + +static void cs_8409_intmike_stream_conn_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12160); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12163); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12166); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this seems to be updating the coef index associated with setConnectionSelect + // unable to figure where this is coming from currently +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0033, 0xffff, 0x00000033, 0, 12168 ); // coef write mask 12168 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0000, 0x00000033, 0x0033, 0 ); // coef write mask 12168 + +} + +static void cs_8409_linein_stream_conn_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // Im thinking of a bugfix here to turn off bit 0x80 of index 0x0009 + + // 0x45 -> 0x23 is line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x023f0600 + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x023f0600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12175); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12178); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12181); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + // this seems to be updating the coef index associated with setConnectionSelect + // unable to figure where this is coming from currently +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0033, 0xffff, 0x00000033, 0, 12183 ); // coef write mask 12183 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0000, 0x00000033, 0x0033, 0 ); // coef write mask 12183 + // possible correct mask to use + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0080, 0x00000033, 0, 0 ); // coef write mask 12183 + +} + +static void cs_8409_intmike_stream_off_nid(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12189); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12192); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12195); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + +} + +static void cs_8409_linein_stream_off_nid(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12197); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 12200); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 12203); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + +} + + +static void cs_8409_intmike_volume_mute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x44 is connected to 0x22 which is labelled mic input (macbook pro) + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x022b2000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x022360b3 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x022360b3 +// snd_hda: amp gain/mute 34 0x60b3 mute 1 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x022b0000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x022350b3 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x022350b3 +// snd_hda: amp gain/mute 34 0x50b3 mute 1 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_mute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x45 which are connected to 0x23 is labelled as line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000a7, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x023360b3 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000060b3); // 0x023360b3 +// snd_hda: amp gain/mute 35 0x60b3 mute 1 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000a7, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00a7 mute 1 gain 0x27 39 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x023350b3 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x000050b3); // 0x023350b3 +// snd_hda: amp gain/mute 35 0x50b3 mute 1 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_intmike_volume_unmute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x44 is connected to 0x22 which is labelled mic input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x022b2000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x022b2000 +// snd_hda: amp gain/mute 34 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 34 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02236033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02236033 +// snd_hda: amp gain/mute 34 0x6033 mute 0 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x022b0000 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x022b0000 +// snd_hda: amp gain/mute 34 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 34 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02235033 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02235033 +// snd_hda: amp gain/mute 34 0x5033 mute 0 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + +static void cs_8409_linein_volume_unmute_nouse(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // nodes 0x45 which are connected to 0x23 is labelled as line input (macbook pro) + + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00002000, 0x000000b3, 0); // 0x023b2000 +// snd_hda: amp gain/mute 35 0x2000 index 0x00 left/right 1 left output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02336033 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00006033); // 0x02336033 +// snd_hda: amp gain/mute 35 0x6033 mute 0 gain 0x33 51 index 0x00 left 1 right 0 output 0 input 1 left input + retval = snd_hda_codec_read_check(codec, spec->linein_amp_nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_AMP_GAIN_MUTE, 0x00000000, 0x000000b3, 0); // 0x023b0000 +// snd_hda: amp gain/mute 35 0x0000 index 0x00 left/right 0 right output/input 0 input +// snd_hda: amp gain/mute 35 0x00b3 mute 1 gain 0x33 51 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02335033 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x00005033); // 0x02335033 +// snd_hda: amp gain/mute 35 0x5033 mute 0 gain 0x33 51 index 0x00 left 0 right 1 output 0 input 1 right input + +} + + +static void cs_8409_setup_TDM_sample_rate(struct hda_codec *codec) +{ + + // codes from windows cs4208_38.inf file + // 0x0001 undocumented (0x0066 = ASP1/2_EN = 1, ASP1/2_STP = 1 ie ASP1_EN = 0x40, ASP2_EN = 0x20, ASP1_STP = 0x4, ASP2_STP = 0x2) + // 0x0005 0x0001 SCDIV 1:4 (0x005a = ASP1: MCEN = 0, FSD = 010 (0x40), SCPOL_IN/OUT = 1 (0x10), SCDIV = 1:4 ie 0x0-0xf) + // 0x0004 0x08ff SC_SRCSEL? = PLL1, LCPR = 0xff (0x28FF = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = FFh), 0x2801 = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = 01h)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setSampleRate: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0xffff, 0x00000200, 0, 25 ); // coef write mask 25 + + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0xffff, 0x00000001, 0, 31 ); // coef write mask 31 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0xffff, 0x000008ff, 0, 37 ); // coef write mask 37 + + // we need to use proper masked versions here - in particular for register 1 which seems to be some form of enable control + // for the subsystems and bits 0x7f need to pass thro here + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x0380, 0x00000200, 0, 0 ); // coef write mask 25 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0x0007, 0x00000001, 0, 0 ); // coef write mask 31 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0x01ff, 0x000008ff, 0, 0 ); // coef write mask 37 +} + +static void cs_8409_setup_TDM_proper_amps12(struct hda_codec *codec) +{ + int ret_coef0 = 0; + int new_coef0 = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + // codes from windows cs4208_38.inf file + // 0x0019 0x0800 = (ASP1.A: TX.LAP = 0, TX.LSZ = 24 bits, TX.LCS = 0) + // 0x001a 0x0820 = (ASP1.A: TX.RAP = 0, TX.RSZ = 24 bits, TX.RCS = 32) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 44 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0019, 0x0800, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 48 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 52 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001a, 0x0820, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 56 + + // codes from windows cs4208_38.inf file + // 0x0000 0xb000 = PLL2_EN (0x2000) PLL1_EN (0x1000) I2C disabled + // 0x0004 0x08ff SC_SRCSEL? = PLL1, LCPR = 0xff (0x28FF = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = FFh), 0x2801 = (ASP1: MC/SC_SRCSEL = PLL1, LCPR = 01h)) + // 0x0000 0x9000 = PLL1_EN (0x1000) I2C disabled + + ret_coef0 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 60 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 64 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0004, 0x0000, 0x000008ff, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 68 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0004, 0x08ff, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 72 + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0x9000, 0x00000000, 76 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 76 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + // it seems we re-write what we read just above here - if never plugin headphones its 0x9000 + // after headphone plugin its 0xb0000 - even after unplugging headphones!! + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, ret_coef0, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + + // codes from windows cs4208_38.inf file + // 0x0003 0x8000 = (ASP1: LCHI = 00h) + // 0x0005 0x0001 = SCDIV = 1:4 (0x005A = (ASP1: MCEN = 0, FSD = 010 (0x40), SCPOL_IN/OUT = 1 (0x10), SCDIV = 1:4)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0003, 0x8000, 0xffff, 0x00008000, 0, 0 ); // coef write mask 80 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0005, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 86 + + // codes from windows cs4208_38.inf file + // 0x0082 0x5401 = ASP1_xxx_EN (0x4000) ASP1_xxx_EN (0x1000) ASP1_xxx_EN (0x400) DMIC1_SCL_EN = 1 (0xFC01 = (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1)) + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5401, 0xffff, 0x00000001, 0, 92 ); // coef write mask 92 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5400, 0x0000, 0x00000001, 0x5401, 0 ); // coef write mask 92 + + // codes from windows cs4208_38.inf file + // 0x0002 0x0280 = ASP2_BUS_IDLE (0x02) GPIO_I2C (0x0A80 = (ASP1/2_BUS_IDLE = 10, +GPIO_I2C)) + + // no evidence this is anything other than 0x0280 yet + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0002, 0x0280, 0xffff, 0x00000280, 0, 0 ); // coef write mask 98 + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 104 + + new_coef1 = (ret_coef1 & 0xffff) | 0x20; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps12 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0220, 0x00000000, 108 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 108 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 108 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 112 ); // coef read 112 + + tdm_in_use(codec, 1); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 117 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 121 + + new_coef71 = (ret_coef71 & 0xffff) | 0x400f; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps12 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 125 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 125 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 125 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 128 +} + +static void cs_8409_setup_TDM_amps12(struct hda_codec *codec, int setrate, int nullformat) +{ + int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + + // this seems to be setup for node 0x02 chain - which seems to use node 0x24 and amps 0x64 and 0x62 (or 0x28 0x2a) + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 +// snd_hda: stream format 2 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00270610 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4033 is Apples fixed format - but this is for boot stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 + } + else + { + // using the stored stream parameters update nid 0x2 stream parameters + // we have limited the allowed formats so should only have working formats here + cs_8409_really_update_stream_format(codec, 0x02, 1, 2, 0); + } + + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 16 + + new_coef1 = (ret_coef1 & 0xffff); // not clear what this is setting - no difference between read and write + // however if used in different places the actual value may be different + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_amps12 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 20 ); // coef write 20 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 20 + + if (setrate) + cs_8409_setup_TDM_sample_rate(codec); + + + cs_8409_setup_TDM_proper_amps12(codec); + + + // enable output node 0x24 + + //retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 132); // 0x024f0700 + retval = snd_hda_codec_read(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000040); // 0x02470740 +// snd_hda: 36 ['AC_PINCTL_OUT_EN'] + +} + + +static void play_setup_TDM_amps12(struct hda_codec *codec, int setrate) +{ + cs_8409_setup_TDM_amps12(codec, setrate, 0); +} + + +static void cs_8409_setup_amp_max(struct hda_codec *codec, int amp_address, int amp_volume, int channel); + +static void cs_8409_setup_amp_ssm3(struct hda_codec *codec, int amp_address, int amp_volume, int channel); + +static void cs_8409_setup_amp_tas576(struct hda_codec *codec, int amp_address, int amp_volume, int channel, int amp_enable); + +static void cs_8409_setup_amp_tas576_amp0(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp1(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp2(struct hda_codec *codec, int amp_volume, int amp_enable); +static void cs_8409_setup_amp_tas576_amp3(struct hda_codec *codec, int amp_volume, int amp_enable); + +static void cs_8409_setup_amps12(struct hda_codec *codec, int amps_enable) +{ + if (codec->core.subsystem_id == 0x106b3900) { + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_max(codec, 0x64, 0x01, 0x00); + cs_8409_setup_amp_max(codec, 0x62, 0x01, 0x02); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + //cs_8409_setup_amp_max(codec, 0x64, 0x30, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x30, 0x02); + // original mapping: + //cs_8409_setup_amp_max(codec, 0x64, 0x30, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x30, 0x01); + //cs_8409_setup_amp_max(codec, 0x64, 0x01, 0x00); + //cs_8409_setup_amp_max(codec, 0x62, 0x01, 0x01); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_ssm3(codec, 0x28, 0x48, 0x00); + cs_8409_setup_amp_ssm3(codec, 0x2a, 0x48, 0x02); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x80, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x80, 0x02); + // original mapping: + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x80, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x80, 0x01); + //cs_8409_setup_amp_ssm3(codec, 0x28, 0x48, 0x00); + //cs_8409_setup_amp_ssm3(codec, 0x2a, 0x48, 0x01); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // NOTE that the TAS57642 amps setup during boot seems to skip the actual speaker power on + // hence the addition of the amps_enable parameter + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_tas576(codec, 0xd8, 0xcf, 0x00, amps_enable); + cs_8409_setup_amp_tas576(codec, 0xda, 0xcf, 0x02, amps_enable); + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + //cs_8409_setup_amp_tas576(codec, 0xd8, 0x9f, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0x9f, 0x02, amps_enable); + // original mapping: + //cs_8409_setup_amp_tas576(codec, 0xd8, 0x9f, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0x9f, 0x01, amps_enable); + //cs_8409_setup_amp_tas576_amp0(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576_amp1(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xd8, 0xcf, 0x00, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xda, 0xcf, 0x01, amps_enable); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void play_setup_amps12(struct hda_codec *codec) +{ + cs_8409_setup_amps12(codec, 1); +} + + +static void cs_8409_setup_TDM_proper_amps34(struct hda_codec *codec) +{ + int ret_coef71 = 0; + int new_coef71 = 0; + + // codes from windows cs4208_38.inf file + // 0x001b 0x0840 = (ASP1.B: TX.LAP = 0, TX.LSZ = 24 bits, TX.LCS = 64) + // 0x001c 0x0860 = (ASP1.B: TX.RAP = 0, TX.RSZ = 24 bits, TX.RCS = 96) + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00008840, 0 ); // coef read 1407 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001b, 0x0840, 0x00000000, 0 ); // coef write 1411 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00008860, 0 ); // coef read 1415 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001c, 0x0860, 0x00000000, 0 ); // coef write 1419 + + // the following actually reads from 0x19 to 0x57 in a loop if the snd_hda_coef_item returns 0 till the read value + // does not have the word sign bit set (ie 0x8000) or finish all 0x57 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 1423 ); // coef read 1423 + + tdm_in_use(codec, 2); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1428 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000400f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 1432 + + new_coef71 = (ret_coef71 & 0xffff) | 0x400f; + myprintk_dbg("snd_hda_intel: masked cs_8409_setup_TDM_proper_amps34 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 1436 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1436 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 1436 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 1439 + +} + +static void cs_8409_setup_TDM_amps34(struct hda_codec *codec, int nullformat) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // this seems to be setup for node 0x03 chain - which seems to use node 0x25 and amps 0x74 and 0x72 (or 0x2c and 0x2e) + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00324033 +// snd_hda: stream format 3 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x00370612 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4033 is Apples fixed format - but this is for boot stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + // note this updates the channel but not stream id???? + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x00224033 + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000002); // 0x00270602 + } + else + { + // using the stored stream parameters update nid 0x3 stream parameters + // we have limited the allowed formats so should only have working formats here + // now realised if we want to pass 2 channel streams to driver then we need to apply same + // fixup as in snd_hda_multi_out_analog_prepare ie set the channel id of the second nid + // to same as first nid for 2 channel inputs - rather than 2 if have a 4 channel stream + mydev_info(hda_codec_dev(codec), "cs_8409_setup_TDM_amps34: stream channels %d\n", spec->stream_channels); + if (spec->stream_channels == 2) + cs_8409_really_update_stream_format(codec, 0x03, 1, 2, 0); + else + cs_8409_really_update_stream_format(codec, 0x03, 1, 2, 2); + } + + cs_8409_setup_TDM_proper_amps34(codec); + + + // enable output on node 0x25 + + //retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 1443); // 0x025f0700 + retval = snd_hda_codec_read(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000040); // 0x02570740 +// snd_hda: 37 ['AC_PINCTL_OUT_EN'] + +} + + +static void play_setup_TDM_amps34(struct hda_codec *codec) +{ + cs_8409_setup_TDM_amps34(codec, 0); +} + +static void cs_8409_setup_amps34(struct hda_codec *codec, int amps_enable) +{ + if (codec->core.subsystem_id == 0x106b3900) { + // use reduced volume - from 0x01 to 0x30 - now passing as argument + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_max(codec, 0x74, 0x01, 0x01); + cs_8409_setup_amp_max(codec, 0x72, 0x01, 0x03); + // use reduced volume - from 0x01 to 0x30 - now passing as argument + //cs_8409_setup_amp_max(codec, 0x74, 0x30, 0x01); + //cs_8409_setup_amp_max(codec, 0x72, 0x30, 0x03); + // original mapping: + //cs_8409_setup_amp_max(codec, 0x74, 0x30, 0x02); + //cs_8409_setup_amp_max(codec, 0x72, 0x30, 0x03); + //cs_8409_setup_amp_max(codec, 0x74, 0x01, 0x02); + //cs_8409_setup_amp_max(codec, 0x72, 0x01, 0x03); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_ssm3(codec, 0x2c, 0x48, 0x01); + cs_8409_setup_amp_ssm3(codec, 0x2e, 0x48, 0x03); + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x80, 0x01); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x80, 0x03); + // original mapping: + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x80, 0x02); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x80, 0x03); + //cs_8409_setup_amp_ssm3(codec, 0x2c, 0x48, 0x02); + //cs_8409_setup_amp_ssm3(codec, 0x2e, 0x48, 0x03); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + // NOTE that the TAS57642 amps setup during boot seems to skip the actual speaker power on + // hence the addition of the amps_enable parameter + // use reduced volume - from 0x48 to 0x80 - same reduction as for MAXs -24dB + // change channel mapping - make a standard left/right pair + // so 1st 2 channels are tweeters, 2nd 2 channels are woofers + cs_8409_setup_amp_tas576(codec, 0xdc, 0xcf, 0x01, amps_enable); + cs_8409_setup_amp_tas576(codec, 0xde, 0xcf, 0x03, amps_enable); + // use reduced volume - from 0xcf to 0x9f - same reduction as for MAXs -24dB (0.5dB steps) + //cs_8409_setup_amp_tas576(codec, 0xdc, 0x9f, 0x01, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0x9f, 0x03, amps_enable); + // original mapping: + //cs_8409_setup_amp_tas576(codec, 0xdc, 0x9f, 0x02, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0x9f, 0x03, amps_enable); + //cs_8409_setup_amp_tas576_amp2(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576_amp3(codec, 0x9f, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xdc, 0xcf, 0x02, amps_enable); + //cs_8409_setup_amp_tas576(codec, 0xde, 0xcf, 0x03, amps_enable); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void play_setup_amps34(struct hda_codec *codec) +{ + cs_8409_setup_amps34(codec, 1); +} + +static void cs_8409_sync_converters_on(struct hda_codec *codec, int nullformat) +{ + int retval; + + struct hda_cvt_setup p2; + struct hda_cvt_setup p3; + + // this stops streaming on nodes 0x2 and 0x3 by switching to stream index 0 + // then updates vendor node coef index 0x0017 twice + // first with 0x1 for node 0x2 and then with 0x2 for node 0x3 + // giving a final 0x3 + // then sets up ready for streaming again + + // so coef index 0x17 is likely turning on the TDM stream + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + // remove normal channel mapping + +// snd_hda: # AppleHDAFunctionGroupCS8409::syncConverters: + //retval = snd_hda_codec_read_check(codec, 0x02, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000010, 2714); // 0x002f0600 + //retval = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONV, 0x00000000); // 0x002f0600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 0)] + + cs_8409_save_and_clear_stream_format(codec, 0x02, &p2); + + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0017, 0x0001, 0xffff, 0x00000000, 0, 0 ); // coef write mask 2716 + + +// snd_hda: # AppleHDAFunctionGroupCS8409::syncConverters: + //retval = snd_hda_codec_read_check(codec, 0x03, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000012, 2722); // 0x003f0600 + //retval = snd_hda_codec_read(codec, 0x03, 0, AC_VERB_GET_CONV, 0x00000000); // 0x003f0600 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00370600 +// snd_hda: conv stream channel map 3 [('CHAN', 0), ('STREAMID', 0)] + + cs_8409_save_and_clear_stream_format(codec, 0x03, &p3); + + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0017, 0x0003, 0xffff, 0x00000001, 0, 0 ); // coef write mask 2724 + + + // and reset back to normal channel mapping + + //snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00270610 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 1)] + + //snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x00370612 +// snd_hda: conv stream channel map 3 [('CHAN', 2), ('STREAMID', 1)] + + if (nullformat) + { + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000002); // 0x00270602 + } + else + { + cs_8409_update_from_save_stream_format(codec, 0x02, &p2, 1, 0); + cs_8409_update_from_save_stream_format(codec, 0x03, &p3, 1, 0); + } +} + + +static void play_sync_converters_on(struct hda_codec *codec) +{ + cs_8409_sync_converters_on(codec, 0); +} + + + +static void cs_8409_setup_amp_max(struct hda_codec *codec, int amp_address, int amp_volume, int channel) +{ + //int retval; + + // NOTA BENE - reduced volume from 0x01 to 0x30 - seems to fit with linux levels much better + // this is semi arbitrary - just hears about OK - not based on rational analysis of amp gains + + + // HPFDCBlocker new as of June 2019 + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1c01 i2c data 0x0001 reg anal: DigitalFilter : HPFDCBlocker +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1008 i2c data 0x0008 reg anal: PCMClockSetup : 256 Bclks +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x14e4 i2c data 0x00e4 reg anal: PCMModeConfig : 32 bits TDM mode 2 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1501 i2c data 0x0001 reg anal: PCMRXEnablesA +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1600 i2c data 0x0000 reg anal: PCMRXEnablesB +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1800 i2c data 0x0000 reg anal: MonoMixChannelSource +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x2d01 i2c data 0x0001 reg anal: DigitalVolCtrl +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x2e05 i2c data 0x0005 reg anal: PathGain +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x4a21 i2c data 0x0021 reg anal: SpeakerEnable : AmpEnabled +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x4d07 i2c data 0x0007 reg anal: RestartBehavior +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5534 i2c data 0x0034 reg anal: LimiterAttackRelease +// snd_hda i2cRead i2c address 0x64 i2c reg 0x1100 i2c data 0x1107 reg anal: PCMSampleSetup : 44.1kHz +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x1107 i2c data 0x0007 reg anal: PCMSampleSetup : 44.1kHz +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x093f i2c data 0x003f reg anal: InterruptClears0 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x0a7f i2c data 0x007f reg anal: InterruptClears1 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x0f0e i2c data 0x000e reg anal: IRQClear1 +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5001 i2c data 0x0001 reg anal: GlobalEnable : Enable + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x001c, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0010, 0x0008, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0014, 0x00e4, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0015, (1<core.subsystem_id == 0x106b3900) { + cs_8409_disable_amp_max(codec, 0x64); + cs_8409_disable_amp_max(codec, 0x62); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + cs_8409_disable_amp_ssm3(codec, 0x28); + cs_8409_disable_amp_ssm3(codec, 0x2a); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //cs_8409_disable_amp_tas576(codec, 0xd8); + //cs_8409_disable_amp_tas576(codec, 0xda); + cs_8409_disable_amp_tas576_amp0(codec); + cs_8409_disable_amp_tas576_amp1(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void playstop_disable_amps12(struct hda_codec *codec) +{ + cs_8409_disable_amps12(codec); +} + +static void cs_8409_disable_TDM_proper_amps12(struct hda_codec *codec) +{ + //int retval; + int ret_coef71 = 0; + int new_coef71 = 0; + + // this seems to be setup for node 0x02 chain - which seems to use node 0x24 and amps 0x64 and 0x62 (or 0x28 and 0x2a) + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 0 ); // coef read 341 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0019, 0x8800, 0x00000000, 0 ); // coef write 345 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00000820, 0 ); // coef read 349 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001a, 0x8820, 0x00000000, 0 ); // coef write 353 + + // snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 357 ); // coef read 357 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 361 ); // coef read 361 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00000840, 365 ); // coef read 365 + + tdm_in_use(codec, 30); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006b, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 370 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000400f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 374 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs_8409_disable_TDM_proper_amps12 coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x400f, 0x00000000, 378 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 378 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 378 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 381 +} + + +static void cs_8409_disable_TDM_amps12(struct hda_codec *codec) +{ + int retval; + + cs_8409_disable_TDM_proper_amps12(codec); + + + // set to defaults and disable output + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00270600 +// snd_hda: conv stream channel map 2 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00220000 +// snd_hda: stream format 2 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 387); // 0x024f0700 + retval = snd_hda_codec_read(codec, 0x24, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x024f0700 + snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02470700 +// snd_hda: 36 [] + +} + +static void playstop_disable_TDM_amps12(struct hda_codec *codec) +{ + cs_8409_disable_TDM_amps12(codec); +} + +static void cs_8409_disable_amps34(struct hda_codec *codec) +{ + if (codec->core.subsystem_id == 0x106b3900) { + cs_8409_disable_amp_max(codec, 0x74); + cs_8409_disable_amp_max(codec, 0x72); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_node_alpha_ssm3(codec); + cs_8409_disable_amp_ssm3(codec, 0x2c); + cs_8409_disable_amp_ssm3(codec, 0x2e); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //cs_8409_disable_amp_tas576(codec, 0xdc); + //cs_8409_disable_amp_tas576(codec, 0xde); + cs_8409_disable_amp_tas576_amp2(codec); + cs_8409_disable_amp_tas576_amp3(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } +} + +static void playstop_disable_amps34(struct hda_codec *codec) +{ + cs_8409_disable_amps34(codec); +} + + +static void cs_8409_disable_TDM_proper_amps34(struct hda_codec *codec) +{ + //int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + + // AppleHDATDMBusManagerCS8409::disableTDMPath + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001b, 0x0000, 0x00000840, 0 ); // coef read 694 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001b, 0x8840, 0x00000000, 0 ); // coef write 698 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001c, 0x0000, 0x00000860, 0 ); // coef read 702 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x001c, 0x8860, 0x00000000, 0 ); // coef write 706 + + // AppleHDATDMBusManagerCS8409::disableTDMPath +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0001, 0xffff, 0x00005401, 0, 710 ); // coef write mask 710 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x5400, 0x00005401, 0x0001, 0 ); // coef write mask 710 + + // AppleHDATDMBusManagerCS8409::disableTDMPath + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000220, 0 ); // coef read 716 + + new_coef1 = (ret_coef1 & 0xffdf); // clear out 0x20 bit + myprintk_dbg("snd_hda_intel: masked cs_8409_disable_TDM_proper_amps34 coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 720 ); // coef write 720 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 720 + + // AppleHDATDMBusManagerCS8409::setupTDMPath or disableTDMPath calls AppleHDATDMBusManagerCS8409::configureTDMUR + // AppleHDATDMBusManagerCS8409::configureTDMUR only place calls this + // this is AppleHDATDMBusManagerCS8409::tdmInUse + // which reads from 0x19 to 0x57 in a loop if the snd_hda_coef_item returns 0 till the read value + // does not have the word sign bit set (ie 0x8000) or finish all 0x57 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 724 ); // coef read 724 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x001a, 0x0000, 0x00008820, 728 ); // coef read 728 + // .... + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0056, 0x0000, 0x00008000, 968 ); // coef read 968 + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0057, 0x0000, 0x00008000, 972 ); // coef read 972 + + tdm_in_use(codec, 40); + + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // coef write 977 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00000000); + +} + +static void cs_8409_disable_TDM_amps34(struct hda_codec *codec) +{ + int retval; + + cs_8409_disable_TDM_proper_amps34(codec); + + + // set to defaults and disable output + // note here we really reset to 0 format in addition to stream id 0/channel id 0 + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00370600 +// snd_hda: conv stream channel map 3 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00320000 +// snd_hda: stream format 3 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 986); // 0x025f0700 + retval = snd_hda_codec_read(codec, 0x25, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000); // 0x025f0700 + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02570700 +// snd_hda: 37 [] + +} + +static void playstop_disable_TDM_amps34(struct hda_codec *codec) +{ + cs_8409_disable_TDM_amps34(codec); +} + + +static void cs_8409_disable_amp_max(struct hda_codec *codec, int amp_address) +{ + //int retval; + + // interrupt read/state read new as of June 2019 + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x64 i2c reg 0x5000 i2c data 0x0000 reg anal: GlobalEnable : Disable +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0300 i2c data 0x0300 reg anal: InterruptState0 +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0400 i2c data 0x0400 reg anal: InterruptState1 +// snd_hda i2cRead i2c address 0x64 i2c reg 0x0c00 i2c data 0x0c00 reg anal: State1 + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0050, 0x0000, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x0003, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x0004, 0); // snd_hda + cs_8409_vendor_i2cRead(codec, amp_address, 0x000c, 0); // snd_hda + +} + + +static void cs_8409_disable_amp_ssm3(struct hda_codec *codec, int amp_address) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cWrite i2c address 0x2c i2c reg 0x0001 i2c data 0x0001 reg anal: PowerControl : PowerDown BVSenseOn + + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0000, 0x0001, 1); // snd_hda + +} + +static void cs_8409_disable_amp_tas576(struct hda_codec *codec, int amp_address) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xd8 i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, amp_address, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, amp_address, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp0(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xd8 i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xd8 i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xd8, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xd8, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp1(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xda i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xda i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xda, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xda, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp2(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xdc i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xdc i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xdc, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xdc, 0x0001, 0x00fc, 0); // snd_hda + +} + +static void cs_8409_disable_amp_tas576_amp3(struct hda_codec *codec) +{ + //int retval; + +// snd_hda: # i2cWrite: +// snd_hda i2cRead i2c address 0xde i2c reg 0x0100 i2c data 0x01fc reg anal: Power Control +// snd_hda i2cWrite i2c address 0xde i2c reg 0x01fc i2c data 0x00fc reg anal: Power Control : Not sleep, Spkr Amp Shutdown + + cs_8409_vendor_i2cRead(codec, 0xde, 0x0001, 0); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0xde, 0x0001, 0x00fc, 0); // snd_hda + +} + + + +static void playstop_disable_amp_max(struct hda_codec *codec, int amp_address) +{ + cs_8409_disable_amp_max(codec, amp_address); +} + + +static void cs_8409_inputs_power_nids_on(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 2785); // 0x022f0500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 2786); // 0x023f0500 + + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + +} + +static void cs_8409_inputs_power_nids_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10741); // 0x022f0500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10742); // 0x023f0500 + + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec); + +static void cs_8409_headset_amp_format_setup_disable(struct hda_codec *codec, int full); + +static void cs43l83_headset_amp_format_setup(struct hda_codec *codec, int set_stream_id, int full); + +static void cs_8409_perform_external_device_unsolicited_responses(struct hda_codec *codec); + +static void cs_8409_plugin_complete_detect(struct hda_codec *codec); + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec); + +static void cs42l83_headset_mike_format_setup_enable(struct hda_codec *codec, int nullformat, int full); + +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec); + + +static int cs_8409_boot_setup_real(struct hda_codec *codec) +{ + int headset_on_boot = 0, retval; + + struct cs8409_apple_spec *spec = codec->spec; + + //hda_nid_t beep_nid = spec->beep_nid; + + mycodec_info(codec, "command cs_8409_boot_setup_real start\n"); + + + setup_reset_and_clear(codec); + + // read parameters from all nodes - excluding VirtualWidgets + + // the loop over node counts calls AppleHDAWidgetFactory::createAppleHDAWidget(DevIdStruct*) + // which Im assuming calls the initForNodeID functions + + init_read_all_nodes(codec); + + read_vendor_node(codec); + + + init_read_coefs(codec); + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + read_virtual_widgets(codec); + + init_for_node_vendor(codec); + + // this is determineSpeakerID + // this does not make sense - this just checks GPIO pin(s)?? + + determine_speaker_id(codec); + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + + // why is this commented?? + //setup_jack_pin_config(codec); + + enable_i2c(codec); + + if (codec->core.subsystem_id == 0x106b3900) { + enable_GPIforUR(codec, 0x5); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + enable_GPIforUR(codec, 0xd); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + enable_GPIforUR(codec, 0xd); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + + if (codec->core.subsystem_id == 0x106b3900) { + cs42l83_external_control_GPIO(codec, 0x7); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + // we have confirmed that the disabling all the cs42l83 setup does not affect the amps + + cs42l83_reset(codec); + + //cs42l83_external_control_GPIO2_clear_2(codec); + //cs42l83_external_control_GPIO2_set_2(codec); + + if (codec->core.subsystem_id == 0x106b3900) { + cs42l83_external_control_GPIO(codec, 0x7); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + cs42l83_external_control_GPIO(codec, 0xf); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + cs42l83_device_id(codec); + + cs42l83_inithw(codec); + + + if (codec->core.subsystem_id == 0x106b3900) { + //setup_amps_reset(codec); + setup_amps_reset_i2c_max(codec); + } + else if (codec->core.subsystem_id == 0x106b3300 || codec->core.subsystem_id == 0x106b3600) { + //setup_amps_reset_i2c_ssm3 + setup_amps_reset_i2c_ssm3(codec); + } + else if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) { + //setup_amps_reset(codec); + setup_amps_reset_i2c_tas576(codec); + } + else { + dev_info(hda_codec_dev(codec), "UNKNOWN subsystem id 0x%08x",codec->core.subsystem_id); + } + + + //read_gpio_status(codec); + + retval = read_gpio_status_check(codec); + + + cs42l83_mic_detect(codec); + + // apparently the imacs use an inverted circuit for physical sensing of jack being plugged in + if (codec->core.subsystem_id == 0x106b1000 || codec->core.subsystem_id == 0x106b0f00 || codec->core.subsystem_id == 0x106b0e00) + cs42l83_tip_sense(codec, 1); + else + cs42l83_tip_sense(codec, 0); + + cs42l83_plugin_interrupt_setup(codec); + + + // should check here if have headphones plugged in + // likely additional code here if we did boot with headphones plugged in + //cs42l83_headphone_sense_data(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real headphone ALREADY PLUGGED IN!!\n"); + // store for after init + headset_on_boot = 1; + } + + // cs_8409_intmike_format_setup_enable is done before we get a capture prepare + // so we need to store some dummy initial setup + // unlike OSX make it a stream id of 0 + // (note this only updates our local cached data - need a cs_8409_really_update_stream_format to actually update nid) + cs_8409_store_stream_format(codec, spec->intmike_adc_nid, 0, 0x4031); + + //setup_intmike_nid(codec); + //cs_8409_intmike_format_setup_format(codec); + cs_8409_intmike_format_setup_enable(codec, 0x4031, 1); + cs_8409_intmike_volume_setup(codec, 0x27); + cs_8409_intmike_stream_on_nid(codec); + + //setup_intmike_format_resetup(codec); + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + //setup_linein_nid(codec); + cs_8409_linein_volume_setup(codec, 0x27); + cs_8409_linein_format_setup_disable(codec); + + //setup_intmike_stream_conn_off_nid(codec); + cs_8409_intmike_stream_conn_off(codec); + + //setup_linein_stream_conn_off_nid(codec); + cs_8409_linein_stream_conn_off(codec); + + //setup_intmike_stream_off_nid(codec); + cs_8409_intmike_stream_off_nid(codec); + + //setup_linein_stream_off_nid(codec); + cs_8409_linein_stream_off_nid(codec); + + //setup_intmike_vol1(codec); + cs_8409_intmike_volume_mute_nouse(codec); + + //setup_linein_vol1(codec); + cs_8409_linein_volume_mute_nouse(codec); + + //setup_intmike_vol2(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol2(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + if (headset_on_boot) + { + // so the big question is what to do about unsolicited responses + // should we disable them in this boot section + // and if so when should we re-enable them + // I suspect at some point the code for headset already plugged in + // switches to unsolicited response code + + // so I think we need to enable unsol response at this stage and block it till end of this function + // - because we seem to get an UNSOL response in the middle of this code + spec->block_unsol = 1; + + // Im guessing the following code should be done explicitly + // because we have not seen a cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int + + // for some reason the stream id is set to 0 here + cs43l83_headset_amp_format_setup(codec, 0, 0); + + //#2880: cs42l83_configure_int_mclk + //#3152: cs42l83_power_onoff + //#3216: cs42l83_configure_serial_port + //#3376: cs42l83_output_set_input_sample_rate + //#3504: cs42l83_setup_audio_output + //#3680: cs42l83_buffers_onoff + + // this is essentially from cs42l83_headset_play_setup_on + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + // so we seem to get an UNSOL response at this point + // - Im thinking we need to store this response + + + // and now turn off - this is now cs42l83_headset_disable + + //#3698: cs42l83_buffers_onoff + //#3714: cs42l83_headset_power_off + + cs42l83_buffers_onoff(codec, 0); + + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // and back on again + // we dont set stream id, do full TDM setup and not enable the pin + // then set stream id, turn on pin but partial TDM setup + cs43l83_headset_amp_format_setup(codec, 0, 1); + cs43l83_headset_amp_format_setup(codec, 1, 0); + + //#3920: cs42l83_configure_int_mclk + //#4192: cs42l83_power_onoff + //#4256: cs42l83_configure_serial_port + //#4416: cs42l83_output_set_input_sample_rate + //#4544: cs42l83_setup_audio_output + //#4720: cs42l83_buffers_onoff + + // this is essentially from cs42l83_headset_play_setup_on + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + //#4738: cs42l83_headphone_sense + + // this may be the same as one of the multiple headphone sense calls seen if no headset plugged in + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real headphone still plugged in!!\n"); + + cs42l83_buffers_onoff(codec, 0); + + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + + cs_8409_intmike_volume_unmute_nouse(codec); + + cs_8409_linein_volume_unmute_nouse(codec); + + cs_8409_intmike_volume_unmute_nouse(codec); + + cs_8409_linein_volume_unmute_nouse(codec); + + cs_8409_inputs_power_nids_off(codec); + + + // so I think we need to try performing the unsol responses here + // - because we get a call to cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int here + // well all the evidence is altho these routines are called they dont actually do anything + // - the cs_8409_read_status_and_clear_interrupt call does not see a TIP SENSE interrupt + // (cf actually plugging in headphones post-boot) + // - calling these routines does clear the interrupt - which may be its only function + + // we need to update headset_phase so we dont ignore headset unsol responses in the following call + spec->headset_phase = 1; + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + spec->block_unsol = 1; + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED - UNIMPLEMENTED!!\n"); + } + + + // so now just going to continue with the logged boot code + // we get a lot of checks the headset is still plugged in + // Im assuming if these detect the headset has been removed other things may happen + // - this would likely be setting up the amps + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 2 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + cs_8409_intmike_volume_unmute(codec); + + cs_8409_linein_volume_unmute(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 3 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + //#5328: cs42l83_headset_button_detect_interrupts_off + //#5392: cs42l83_headset_set_hpout_clamp_disable + + // these 2 functions seem to be this function + // - additional code is setting jack_present and an msleep + cs_8409_plugin_handle_detect(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 4 - UNIMPLEMENTED!!\n"); + } + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + // so there are 4 gpio status reads here + // why 4?? + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + // why - this just sets nid 0x22 format??? + cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + + //cs_8409_linein_volume_setup_new(codec, 0x27) + cs_8409_linein_volume_setup(codec, 0x27); + + + cs_8409_linein_format_setup_disable(codec); + + cs_8409_intmike_stream_conn_off(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + cs_8409_linein_stream_off_nid(codec); + + cs_8409_intmike_volume_mute(codec); + cs_8409_linein_volume_mute(codec); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 5 - UNIMPLEMENTED!!\n"); + } + + // we see a delay here - this is I think the equivalent of the 1800 msleep + // in cs_8409_headset_plugin_event + // should we just make it 1800?? + msleep(1500); // what we actually see is 1470 + + // the cs_8409_plugin_complete_detect function seems to encapsulate what happens next + // - this functions ends with initiating headset detection + //#5551: cs42l83_headphone_sense + //#5567: cs42l83_complete_jack_detect + //#5631: cs42l83_power_hs_bias_on + //#5727: cs42l83_enable_hs_auto_int_on + //#5759: cs42l83_unplug_interrupt_setup + //#5807: cs42l83_set_hpout_pulldown_off + //#5839: cs42l83_headset_detect_on + + cs_8409_plugin_complete_detect(codec); + + spec->block_unsol = 1; + + // testing status checks to see if we get an interrupt + // so at this point the status is still 0x27 - no interrupt + // can we just use the msleep?? + retval = read_gpio_status_check(codec); + + msleep(1); + + // but here the status is 0x26 ie interrupt + //retval = read_gpio_status_check(codec); + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + + // so next we should get some unsol responses + //#5923: cs42l83_read_status_and_clear_interrupt + //#6117: cs42l83_disambiguate_ur_from_int + //#6165: cs_8409_headset_type_detect_event + //#6165: cs42l83_enable_hs_auto_int_off + //#6197: cs42l83_headset_type + //#6213: cs42l83_unplug_headset_detect_off + //#6277: cs42l83_set_hpout_pulldown_onoff + //#6309: cs42l83_set_hpout_clamp_enable + //#6341: cs42l83_enable_hsbias_auto_clamp_on + //#6373: cs42l83_enable_hsbias_auto_clamp_off + //#6421: cs42l83_power_hs_bias_off + //#6517: cs42l83_setup_button_detect + //#6709: cs42l83_power_hs_bias_button_on + //#6805: cs42l83_enable_hsbias_auto_clamp_off1 + + cs_8409_perform_external_device_unsolicited_responses(codec); + + + retval = read_gpio_status_check(codec); + + // so the delay here seems to be around 130 ms + // with delay of 1 ms dont see any interrupts + msleep(130); + + retval = read_gpio_status_check(codec); + + + //#6857: cs42l83_read_status_and_clear_interrupt + //#7051: cs42l83_disambiguate_ur_from_int + // cs_8409_headset_button_detect_event + //#7099: cs42l83_handle_button_detect + //#7243: cs42l83_mike_connected + + // cs_8409_perform_external_device_unsolicited_responses then calls cs_8409_plugin_event_continued + // - but here we have a divergence from plugin post-boot + // fixed up cs_8409_plugin_event_continued to only do things for plugin post-boot + // - at boot we drop back to here + // NOTA BENE - MUST set up the button interrupts here now - otherwise buttons wont work + + cs_8409_perform_external_device_unsolicited_responses(codec); + + + //#7553: cs_8409_enable_headset_streaming + //#7553: cs43l83_headset_amp_format_setup + //#7417: cs42l83_headset_play_setup_on + //#7417: cs42l83_configure_int_mclk + //#7689: cs42l83_power_onoff + //#7753: cs42l83_configure_serial_port + //#7913: cs42l83_output_set_input_sample_rate + //#8041: cs42l83_setup_audio_output + //#8217: cs42l83_buffers_onoff + + cs43l83_headset_amp_format_setup(codec, 1, 1); + cs42l83_headset_play_setup_on(codec); + + // and yet again turn off + //#8233: cs42l83_buffers_onoff + //#8249: cs42l83_power_onoff + //#8279: cs_8409_headset_amp_format_setup_disable + + cs42l83_buffers_onoff(codec, 0); + cs42l83_power_onoff(codec, 0); + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // and back on again + // we dont set stream id, do full TDM setup and not enable the pin + // then set stream id, turn on pin but partial TDM setup + cs43l83_headset_amp_format_setup(codec, 0, 1); + cs43l83_headset_amp_format_setup(codec, 1, 0); + + + //#8455: cs42l83_headset_play_setup_on + //#8455: cs42l83_configure_int_mclk + //#8727: cs42l83_power_onoff + //#8791: cs42l83_configure_serial_port + //#8951: cs42l83_output_set_input_sample_rate + //#9079: cs42l83_setup_audio_output + //#9255: cs42l83_buffers_onoff + + cs42l83_headset_play_setup_on(codec); + + + //#9273: cs42l83_headphone_sense + + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + if ((retval & 0x80)) + { + //#9287: cs_8409_intmike_stream_conn_off_disable + //#9299: cs_8409_linein_stream_conn_off + + cs_8409_intmike_stream_conn_off(codec); + cs_8409_linein_stream_conn_off(codec); + + // snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + // snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + // snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + // snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + + + //#9324: cs_8409_intmike_volume_setup + //#9357: cs_8409_intmike_format_setup_disable + //#9324: cs_8409_linein_volume_setup + //#9401: cs_8409_linein_format_setup_disable + //#9412: cs42l83_headset_mike_format_setup_enable + //#9451: cs42l83_input_set_output_sample_rate + //#9579: cs42l83_mike_setup_audio_input + //#9643: cs42l83_mike_enable + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_format_setup_disable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + + + // as the following relates to the headset mike + // Im guessing we should only do it if we have a mike + // spec->have_mike should have been set above + // by the unsol response function cs_8409_headset_type_detect_event + + if (spec->have_mike) + { + + // this is part of cs_8409_enable_headset_mike_streaming + + // whats missing is no actual cs42l83 power on calls + // or unmuting input + + cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + cs42l83_input_set_output_sample_rate(codec); + + cs42l83_mike_setup_audio_input(codec); + + cs42l83_mike_enable(codec); + + + // and disable it all!! + // this is part of cs_8409_disable_headset_mike_streaming + // as usual disabling this duplicate setup + + //#9675: cs42l83_mike_disable + //#9705: cs42l83_headset_mike_format_setup_disable + + //cs42l83_mike_disable(codec); + + //cs42l83_headset_mike_format_setup_disable(codec); + + + //#9738: cs42l83_headset_mike_format_setup_enable + //#9738: cs42l83_headset_mike_format_setup_enable + //#9810: cs42l83_input_set_output_sample_rate + //#9938: cs42l83_mike_setup_audio_input + //#10002: cs42l83_mike_enable + + // why duplicate setup - 1st dont set stream id/pin + // 2nd set stream id and pin + //cs42l83_headset_mike_format_setup_enable(codec, 0, 0); + //cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + //cs42l83_input_set_output_sample_rate(codec); + + //cs42l83_mike_setup_audio_input(codec); + + //cs42l83_mike_enable(codec); + + //// this doesnt make sense double read of nid 0x3c then double set?? + //retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 25649); // 0x03cf0700 + //retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 25650); // 0x03cf0700 + //snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + //// snd_hda: 60 ['AC_PINCTL_IN_EN'] + //snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + //// snd_hda: 60 ['AC_PINCTL_IN_EN'] + + // is this a good position to switch the inputs?? + // just before enable buttons is where we switch inputs on plugin + // note that this is just telling Alsa the input source has changed + // - no changes to audio setup at all + switch_input_src(codec); + + + //#10040: cs_8409_headset_mike_buttons_enable + //#10040: cs42l83_configure_headset_button_interrupts + //#10088: cs42l83_enable_hsbias_auto_clamp_off2 + //#10136: cs42l83_enable_hsbias_auto_clamp_on3 + + cs_8409_headset_mike_buttons_enable(codec); + + + // we get some calls to cs42l83_read_status_and_clear_interrupt/cs42l83_disambiguate_ur_from_int here + // clear out any stored unsol responses + + // I think we need to block here - because clearing the interrupts causes interrupts!! + // this is similar to cs_8409_cs42l83_unsolicited_response where we block + // before calling the perform function + spec->block_unsol = 1; + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + } + + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot headphone REMOVED 6 - UNIMPLEMENTED!!\n"); + } + + + spec->block_unsol = 0; + + dev_info(hda_codec_dev(codec), "cs_8409_boot_setup_real boot CURRENT IMPLEMENTATION END!!\n"); + + } + else + { + + // NOTE - OSX sets a stream format here but a null (ie 0) stream id + // on linux we set the OSX format - it will be updated with actual stream format later + + //setup_TDM_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + //amps_enable2_6462(codec); + cs_8409_setup_amps12(codec, 0); + + //setup_TDM_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_enable2_7472(codec); + cs_8409_setup_amps34(codec, 0); + + //sync_converters(codec); + cs_8409_sync_converters_on(codec, 1); + + + //sync_converters1(codec); + cs_8409_sync_converters_off(codec, 1); + + //amps_disable_6462(codec); + cs_8409_disable_amps12(codec); + + //putative_tdm_disable_6462(codec); + cs_8409_disable_TDM_amps12(codec); + + //amps_disable_7472(codec); + cs_8409_disable_amps34(codec); + + //putative_tdm_disable_7472(codec); + cs_8409_disable_TDM_amps34(codec); + + + // I think Im going to disable the following as we appear to have a stream id here + // but under linux we do not have a stream at all at this boot stage + + if (0) + { + + // this is not quite correct - cs_8409_setup_TDM_amps12 will write a null stream id etc + // but the actual logged version does not - although does update format + // this may be because Apple is caching the stream format/id similar to linux and + // at this point we have already set a null stream id - but because the above disable also + // cleared the format we get a re-setup of the the format + //putative_enable1_TDM_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + //amps_disable2_6462(codec); + cs_8409_disable_amps12(codec); + + // see above notes for putative_enable1_TDM_6462 + //putative_enable1_TDM_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_disable2_7472(codec); + cs_8409_disable_amps34(codec); + + // so this does not set the channel id for node 0x03 to 0x2 but cs_8409_sync_converters_on does + // is this significant?? + // does suggest this function reads the initial stream id then rewrites at the end + //sync_converters2(codec); + cs_8409_sync_converters_on(codec, 1); + + + // so this also not quite same - we actually have a stream id here on OSX + // but at the boot stage dont think we have this in linux + //enable2_TDM2_6462(codec); + cs_8409_setup_TDM_amps12(codec, 1, 1); + + + //amps_enable2_6462(codec); + cs_8409_setup_amps12(codec, 0); + + // see above + //enable2_TDM2_7472(codec); + cs_8409_setup_TDM_amps34(codec, 1); + + //amps_enable2_7472(codec); + cs_8409_setup_amps34(codec, 0); + + //sync_converters3(codec); + cs_8409_sync_converters_on(codec, 1); + + + // I dont get this - sync_converters3 sets the stream id/channel id to non-zero + // but here when we read the stream id/channel id its 0?? + //sync_converters4(codec); + cs_8409_sync_converters_off(codec, 1); + + //amps_disable3_6462(codec); + cs_8409_disable_amps12(codec); + + //putative_disable3_TDM_6462(codec); + cs_8409_disable_TDM_amps12(codec); + + //amps_disable3_7472(codec); + cs_8409_disable_amps34(codec); + + //putative_disable3_TDM_7472(codec); + cs_8409_disable_TDM_amps34(codec); + } + + + // this is best guess what these volume functions are doing + // as from the log there is no change in output volume or muting + // - but if already unmuted thats what you would expect + + //setup_mic_vol2(codec); + + //setup_intmike_vol3(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol3(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + + //setup_mic_vol3(codec); + + //setup_intmike_vol4(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol4(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + + //setup_mic_vol3(codec); + //setup_input_power_nids_off(codec); + cs_8409_inputs_power_nids_off(codec); + + + //read_gpio_status1(codec); + + //read_gpio_status2(codec); + + //read_gpio_status3(codec); + + // why 3 reads here - they seem to return the exact same data each time + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + retval = read_gpio_status_check(codec); + + + //cs42l83_headphone_sense1(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 1 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + + //setup_mic_vol4(codec); + + //setup_intmike_vol5(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol5(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + //setup_mic_vol5(codec); + + //setup_intmike_vol6(codec); + cs_8409_intmike_volume_unmute_nouse(codec); + + //setup_linein_vol6(codec); + cs_8409_linein_volume_unmute_nouse(codec); + + //cs42l83_headphone_sense2(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 2 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + + //cs42l83_headphone_sense3(codec); + retval = cs42l83_headphone_sense(codec); + mycodec_info(codec, "cs_8409_boot_setup_real headphone sense 3 0x%x %s\n", retval, ((retval & 0x80)?"plugged in":"not plugged in")); + + //setup_intmike_nid1(codec); + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + //setup_linein_vol7(codec); + cs_8409_linein_volume_setup(codec, 0x27); + + //setup_linein_nid1(codec); + cs_8409_linein_format_setup_disable(codec); + + //setup_intmike_stream_conn_off_nid1(codec); + cs_8409_intmike_stream_conn_off(codec); + + //setup_linein_stream_conn_off_nid1(codec); + cs_8409_linein_stream_conn_off(codec); + + //setup_intmike_stream_off_nid1(codec); + cs_8409_intmike_stream_off_nid(codec); + + //setup_linein_stream_off_nid1(codec); + cs_8409_linein_stream_off_nid(codec); + + + //setup_intmike_vol10(codec); + //cs_8409_volume_set(codec, 0x22, 0x33); + cs_8409_volume_set(codec, spec->intmike_adc_nid, 0x33); + + //setup_linein_vol10(codec); + //cs_8409_volume_set(codec, 0x23, 0x33); + cs_8409_volume_set(codec, spec->linein_amp_nid, 0x33); + } + + mycodec_info(codec, "command cs_8409_boot_setup_real end\n"); + + return 0; +} + + + +static void cs_8409_play_real(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + //cs_8409_play_data(codec); + + mycodec_info(codec, "command cs_8409_play_real start"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + play_setup_TDM_amps12(codec, 1); + + play_setup_amps12(codec); + + + play_setup_TDM_amps34(codec); + + play_setup_amps34(codec); + + + play_sync_converters_on(codec); + + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_play_real end"); + +} + + + + +static void cs_8409_amps_disable_streaming(struct hda_codec *codec) +{ + //int retval; + mycodec_info(codec, "cs_8409_amps_disable_streaming start\n"); + + playstop_sync_converters_off(codec); + + playstop_disable_amps12(codec); + + playstop_disable_TDM_amps12(codec); + + playstop_disable_amps34(codec); + + playstop_disable_TDM_amps34(codec); + + // for some reason Apple duplicates the amp disable here?? + + playstop_disable_amps12(codec); + + playstop_disable_amps34(codec); + + + mycodec_info(codec, "cs_8409_amps_disable_streaming end\n"); +} + +static void cs_8409_playstop_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + mycodec_info(codec, "command cs_8409_playstop_real start"); + + //cs_8409_playstop_data(codec); + + cs_8409_amps_disable_streaming(codec); + + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + // weird - why set the inputs to powered off when stop playing?? + cs_8409_inputs_power_nids_off(codec); + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_playstop_real end"); +} + + + +static void cs_8409_capture_real(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + //cs_8409_capture_data(codec); + + mycodec_info(codec, "command cs_8409_capture_real start"); + + retval = snd_hda_codec_read_check(codec, 0x00, 0, AC_VERB_PARAMETERS, 0x00000000, 0x10138409, 1); // 0x000f0000 + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + // NOTE - there are big ordering issues here + // - here we setup the speaker output before the internal mike + // - this maybe because Quicktime defaults to enabling play when recording + // unfortunately looks as tho linux tends to open the capture stream before the playback stream + // - so going to ignore this here + + + cs_8409_inputs_power_nids_on(codec); + + //cs_8409_intmike_format_setup_format(codec); + cs_8409_intmike_format_setup_enable(codec, 0x4031, 0); + + cs_8409_intmike_volume_setup(codec, 0x27); + cs_8409_intmike_stream_on_nid(codec); + + cs_8409_intmike_volume_unmute(codec); + cs_8409_linein_volume_unmute(codec); + + // so here we get AMP_GAIN_MUTE setups but nothing changes + // - so either this is a volume update with no change or unmute with no change + // - which to do with?? + //cs_8409_intmike_volume_setup - (no change) + //cs_8409_linein_volume_setup - (no change) + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_capture_real end"); + +} + +static void cs_8409_intmike_stream_reset_nid_on(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // OK dont get this - we turn the stream back on for the internal mike + // - but assume pin is OK?? + // now think these 2 functions are resetting to original state - which happens + // to be stream on for intmike and stream off for linein + // NOT PROPERLY FUNCTIONAL YET!!!!! + +// retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 23); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x02270610 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 1)] + +} + +static void cs_8409_linein_stream_reset_nid_off(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // OK dont get this - we turn the stream off for the linein + // - but assume pin is OK?? + +// retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 25); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02370600 +// snd_hda: conv stream channel map 35 [('CHAN', 0), ('STREAMID', 0)] + +} + + +static void cs_8409_capturestop_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + + // so I have seen an UNSOL response in cs_8409_playstop_real + // which suggests need to block responses here + spec->block_unsol = 1; + + + mycodec_info(codec, "command cs_8409_capturestop_real start"); + + //cs_8409_capturestop_data(codec); + + + cs_8409_intmike_stream_conn_off(codec); + cs_8409_linein_stream_conn_off(codec); + + // this I think is re-setting to pre-capture state + // THIS NEEDS FIXING!!! + cs_8409_intmike_stream_reset_nid_on(codec); + cs_8409_linein_stream_reset_nid_off(codec); + + + cs_8409_intmike_volume_set(codec, 0x27); + cs_8409_intmike_volume_mute(codec); + + // we also reset the pin - however no change to volume or unmute + // so cant say if should just set unmute or set volume to 0 + // just choosing one + //cs_8409_volume_set(codec, 0x44, 0x00); + cs_8409_volume_set(codec, spec->intmike_nid, 0x00); + + + cs_8409_intmike_format_setup_disable(codec); + + + cs_8409_linein_volume_set(codec, 0x27); + cs_8409_linein_volume_mute(codec); + + // we also reset the pin - however no change to volume or unmute + // so cant say if should just set unmute or set volume to 0 + // just choosing one + //cs_8409_volume_set(codec, 0x45, 0x00); + cs_8409_volume_set(codec, spec->linein_nid, 0x00); + + + cs_8409_linein_format_setup_disable(codec); + + + cs_8409_inputs_power_nids_off(codec); + + + // using Quicktime we get a play disable when we stop recording + //cs_8409_sync_converters_off + //cs_8409_disable_amps12 + //cs_8409_disable_TDM_amps12 + //cs_8409_disable_amps34 + //cs_8409_disable_TDM_amps34 + //cs_8409_disable_amps12 + //cs_8409_disable_amps34 + + + cs_8409_perform_external_device_unsolicited_responses(codec); + + spec->block_unsol = 0; + + + mycodec_info(codec, "command cs_8409_capturestop_real end"); +} + + +// this is the function which handles unsolicited responses +// - which seem to be enabled by default on the 8409 and set to be from GPIO pin 1 +// which seems to be handling the cs42l83 + +static int cs_8409_read_status_and_clear_interrupt(struct hda_codec *codec); +static int cs42l83_disambiguate_ur_from_int(struct hda_codec *codec); +static void cs_8409_interrupt_action(struct hda_codec *codec, int int_response); + +static void cs_8409_external_device_unsolicited_response(struct hda_codec *codec) +{ + int retval; + int retval_int; + int ret_disambig; + int int_response; + int int_masked; + int intcnt = 0; + + retval = read_gpio_status_check(codec); + + if ((retval & 0x01) == 0x01) + { + mycodec_info(codec, "cs_8409_external_device_unsolicited_response - interrupt clear\n"); + return; + } + + mycodec_info(codec, "cs_8409_external_device_unsolicited_response - interrupt set\n"); + + // so retval_int is a bit shifted combination of a number of primary interrupt status registers + + retval_int = cs_8409_read_status_and_clear_interrupt(codec); + + // and ret_disambig is the same bit shifted combination of a number of primary interrupt mask registers + + ret_disambig = cs42l83_disambiguate_ur_from_int(codec); + + // move prints to after so not spaced by other prints + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL interrupt 0x%08x\n",retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL disambig 0x%08x\n",ret_disambig); + + // determine masked interrupts + + int_masked = (ret_disambig & retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL masked 0x%08x\n",int_masked); + + // determine unmasked interrupts + + int_response = ((~ret_disambig) & retval_int); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL unmasked 0x%08x\n",int_response); + + intcnt = hweight_long(int_response); + + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL number interrupt actions %d\n", intcnt); + + + // do we call a mapping function here?? + + if (int_response != 0) { + + cs_8409_interrupt_action(codec, int_response); + + } + else + codec_info(codec, "cs_8409_external_device_unsolicited_response - UNSOL NO unmasked interrupt\n"); + + + return; +} + + +static int cs42l83_read_status_and_clear_interrupt(struct hda_codec *codec); + +static int cs_8409_read_status_and_clear_interrupt(struct hda_codec *codec) +{ + int retval = 0; + int retint = 0; + int last_retint = 0; + int loopmax = 11; + int loopcnt = 0; + + // AppleHDAFunctionGroupCS8409::readStatusAndClearInterrupt + + mycodec_info(codec, "cs_8409_read_status_and_clear_interrupt start\n"); + + retval = read_gpio_status_check(codec); + + if ((retval & 0x01) == 0x01) + { + mycodec_info(codec, "cs_8409_read_status_and_clear_interrupt - interrupt %d clear\n",loopcnt); + return 0; + } + + // so this function causes an unsol response because of clearing the interrupt + // what this means is we need to add a check in the unsol response callbacks + // to ignore GPIO 1 changes from 0 to 1 - 1 seems to be the default + // and a 1 to 0 transition means interrupt has been triggered + + while (1) + { + + retint = cs42l83_read_status_and_clear_interrupt(codec); + + codec_info(codec, "cs_8409_read_status_and_clear_interrupt - UNSOL status 0x%08x\n",retint); + + retval = read_gpio_status_check(codec); + + if (loopcnt >= loopmax) + { + dev_info(hda_codec_dev(codec), "cs_8409_read_status_and_clear_interrupt - ERROR - max count exceeded\n"); + break; + } + + loopcnt++; + + if ((retval & 0x01) == 0x01) + { + codec_info(codec, "cs_8409_read_status_and_clear_interrupt - interrupt %d clear\n",loopcnt); + break; + } + + last_retint = retint; + + // so this code definitely has a 10 IOSleep sleep call ie 10 ms + // but from the logs it is much closer to 50 ms + // IOSleep(10); + //usleeprange(10000,12000); + msleep(10); + + } + + codec_info(codec, "cs_8409_read_status_and_clear_interrupt 0x%08x end\n",retint); + + return retint; +} + +static int cs42l83_read_status_and_clear_interrupt(struct hda_codec *codec) +{ + int retval; + int retval_det1; + int retval_det2; + int retval_cdc; + + // AppleHDATDM_CS42L83::readStatusAndClearInterrupt + + // so I think Ive finally figured whats going on with interrupts and the 8409 + // cs42l83 interrupts trigger a state change on GPIO pin 1 which is set to + // trigger an Unsolicited Response (UR) (enableGPIforUR function) + // so we get a UR from high(1) (default) to low(0) when the interrupt is set + // plus a UR from low(0) to high(1) when the interrupt is cleared + // interrupt clearing seems to be triggered by reading registers in this routine (hence the name) + // Im now pretty certain 0x1b7b and 0x1b7c indicate which interrupt of + // 0x1b79 and 0x1b7a (Detect Interrupt Mask 1 and Detect Interrupt Mask 2) + // was triggered + + // Im suspecting the 0x1b7b (maybe 0x1b7c) which are undocumented + // - the other registers seem to be flagged as status registers + // 0x1b7b is listed in figure 4-45 as an interrupt register but otherwise undocumented!! + + // moved to above + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // note that a lot of i2cPagedRead followed by i2cPagedWrite are likely AppleHDATDMDevice::maskWriteReg + // ie read from register and mask of bits and set certain bits without affecting others + // we do not know the mask being used from the HDA commands!! + + // finally know what ASP refers to in lot of Cirrus docs - Audio Serial Port + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Detect Interrupt 1 Status + // Detect Interrupt 1 Status + // value 0x40 (TIP_SENSE_PLUG interrupt from 0xa0 TIP_SENSE_PLUG unmasked) + // register 0x1b7c - this is undocumented for 42l42 (reserved) + // Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xff state of 0x1b7a) + // register 0x1308 - Codec Interrupt Status + // value 0x01 - Headset disabled, Powered down + // register 0x1301 - ADC Overflow Interrupt Status + // register 0x1302 - Mixer Interrupt Status + // register 0x1303 - SRC Interrupt Status + // register 0x1304 - ASP RX Interrupt Status + // register 0x1305 - ASP TX Interrupt Status + // register 0x130b - SRC Partial Lock Interrupt Status + // register 0x130d - VP Monitor Interrupt Status + // register 0x130e - PLL Lock Interrupt Status + // register 0x130f - Tip/Ring Sense Plug/Unplug Interrupt Status + // value 0x00 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b40 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0800 i2c data 0x0801 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0200 i2c data 0x0200 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0300 i2c data 0x030c +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0400 i2c data 0x0400 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0500 i2c data 0x0500 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0d00 i2c data 0x0d01 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0e00 i2c data 0x0e00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0f00 i2c data 0x0f00 + + mycodec_info(codec, "cs42l83_read_status_and_clear_interrupt start\n"); + + // note that these functions contain power on/power off calls + + retval_det1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval_det2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + retval_cdc = cs_8409_vendor_i2cRead(codec, 0x90, 0x1308, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1301, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1302, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1303, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1304, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1305, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130d, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130f, 1); // snd_hda + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x00170503 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D3); + + mycodec_info(codec, "cs42l83_read_status_and_clear_interrupt end\n"); + + retval = 0; + retval = ((retval_det1 & 0xff) << 16) | ((retval_det2 & 0xff) << 8) | (retval_cdc & 0xff); + + return retval; +} + +static int cs42l83_disambiguate_ur_from_int(struct hda_codec *codec) +{ + int retval_det1; + int retval_det2; + int retval_cdc; + int retval; + + // from AppleHDAFunctionGroupCS8409::externalDeviceUnsolicitedResponse + + // AppleHDATDM_CS42L83::disambiguateURfromINT + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // register 0x1b79 - Detect Interrupt Mask 1 + // value 0xa0 TIP_SENSE_PLUG unmasked + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xff all masked + // register 0x131b - Codec Interrupt Mask + // value 0x3 all masked + + mycodec_info(codec, "cs42l83_disambiguate_ur_from_int start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79a0 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 + + retval_det1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + retval_det2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + retval_cdc = cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + + mycodec_info(codec, "cs42l83_disambiguate_ur_from_int end\n"); + + // there is a bunch of code here presumably figuring out what happened + + retval = 0; + retval = ((retval_det1 & 0xff) << 16) | ((retval_det2 & 0xff) << 8) | (retval_cdc & 0xff); + return retval; + +} + + +static void cs42l83_set_power_state_on(struct hda_codec *codec, int instate) +{ + int retval; + int loopcnt; + + mycodec_info(codec, "cs42l83_set_power_state_on start\n"); + + // likely in AppleHDATDM_CS42L83::enable + // (only place AppleHDATDM_CS42L83::setPowerState is called from is AppleHDATDM_CS42L83::enable) + // AppleHDATDM_CS42L83::setPowerState + + // register 0x1101 - Power Down Control 1 + // register 0x130b - SRC Partial Lock Interrupt Status + + // seems to me this powers on either the input path ie mike (instate=1) + // or output path ie headphones (instate=0) + + // I think that the use of cs_8409_vendor_i2cWriteMask means that we can + // call this function multiple times and set both input and output power on + // because only the bits in the mask are affected + +// codec headphone (digital in/audio out) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01fe +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + +// codec capture (audio in/digital out) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0196 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0116 i2c data 0x0016 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b24 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0116 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0112 i2c data 0x0012 + + + if (instate) + { + // power setup for codec (0x01) and output (0x80) + + // this converts 0xfe to 0x7e (or 0x96 to 0x16) + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x81, 0x0, 1); // snd_hda + + loopcnt = 0; + while (loopcnt < 0x14) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + if ((retval & 0x1) == 0x1) + break; + loopcnt++; + // IOSleep(2); + msleep(2); + } + + // power setup for ADC (0x04) + + // this converts 0x7e to 0x7a (or 0x16 to 0x12) + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x04, 0x0, 1); // snd_hda + } + else + { + // power setup for codec (0x01), input (0x40) and mixer (0x20) + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + // this converts 0xfe to 0x9e + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x61, 0x0, 1); // snd_hda + + loopcnt = 0; + while (loopcnt < 0x14) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + if ((retval & 0x4) == 0x4) + break; + loopcnt++; + // IOSleep(2); + msleep(2); + } + + // power setup for headphone (0x08) + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + + // this converts 0x9e to 0x96 + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x08, 0x0, 1); // snd_hda + + } + + mycodec_info(codec, "cs42l83_set_power_state_on end\n"); +} + +static void cs42l83_headset_amp_setup_TDM_sample_rate(struct hda_codec *codec) +{ + +// snd_hda: # AppleHDATDMBusManagerCS8409::setSampleRate: + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0xffff, 0x00000200, 0, 3892 ); // coef write mask 3892 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0042, 0xffff, 0x00000040, 0, 3898 ); // coef write mask 3898 + //snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0xffff, 0x000010ff, 0, 3904 ); // coef write mask 3904 + + // we need to use proper masked versions here - in particular for register 1 which seems to be enabling the Audio Serial Port + // for the subsystems and bits 0x7f need to pass thro here + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x0380, 0x00000200, 0, 0 ); // coef write mask 25 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0042, 0x0007, 0x00000040, 0, 0 ); // coef write mask 3898 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0x01ff, 0x000010ff, 0, 0 ); // coef write mask 3904 + +} + +static void cs42l83_headset_amp_setup_TDM_proper(struct hda_codec *codec, int full) +{ + //int retval; + int ret_coef0 = 0; + int new_coef0 = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + + if (full) + { + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 3810 + new_coef1 = (ret_coef1 & 0xffff); // not clear what this is setting - no difference between read and write so far + // however if used in different places the actual value may be different + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_setup_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 3814 ); // coef write 3814 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 3814 ); // coef write 3814 + } + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0015, 0xaaaa, 0xffff, 0x0000aaaa, 0, 0 ); // coef write mask 3819 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0014, 0x0100, 0xffff, 0x00000000, 0, 0 ); // coef write mask 3825 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00008000, 0 ); // coef read 3832 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0029, 0x0800, 0x00000000, 0 ); // coef write 3836 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00008000, 0 ); // coef read 3840 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x002a, 0x0820, 0x00000000, 0 ); // coef write 3844 + + if (full) + { + ret_coef0 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0000, 0x0000, 0x00009000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 3848 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3852 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0007, 0x0000, 0x000028ff, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef read 3856 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0007, 0x10ff, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3860 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, 0xb000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 3864 + // NOTA BENE - here we update from 0x9000 to 0xb000 - which is then never removed - even after unplugging headphones!! + new_coef0 = ret_coef0 | 0x2000; + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0000, new_coef0, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::setupTDMPath coef write 76 + + // snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0006, 0x8000, 0xffff, 0x00008000, 0, 0 ); // coef write mask 3868 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0008, 0x0040, 0xffff, 0x00000000, 0, 0 ); // coef write mask 3874 + + // snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + // snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0xa801, 0xffff, 0x00000001, 0, 3880 ); // coef write mask 3880 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0xa800, 0x0000, 0x00000001, 0xa801, 0 ); // coef write mask 3880 + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0002, 0x0280, 0xffff, 0x00000280, 0, 0 ); // coef write mask 3886 + + cs42l83_headset_amp_setup_TDM_sample_rate(codec); + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000200, 0 ); // coef read 3910 + + new_coef1 = (ret_coef1 & 0xffff) | 0x40; + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_setup_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0240, 0x00000000, 3914 ); // coef write 3914 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // coef write 3914 + } + + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 3918 ); // coef read 3918 + + tdm_in_use(codec, 1); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3987 + + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 3991 + + new_coef71 = (ret_coef71 & 0xffff) | 0x800f; + myprintk_dbg("snd_hda_intel: masked play_setup_TDM_proper_full_headphone coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 3995 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3995 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 3995 + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 3998 + +} + +static void cs43l83_headset_amp_format_setup(struct hda_codec *codec, int set_stream_id, int full) +{ + int retval; + + mycodec_info(codec, "cs43l83_headset_amp_format_setup set_stream_id %d full %d\n", set_stream_id, full); + + //snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x00a24031 +// snd_hda: stream format 10 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + if (set_stream_id) + { + //snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x00a70610 + // snd_hda: conv stream channel map 10 [('CHAN', 0), ('STREAMID', 1)] + + // using the stored stream parameters update nid 0x0a stream parameters + // we have limited the allowed formats so should only have working formats here + cs_8409_really_update_stream_format(codec, 0x0a, 1, 2, 0); + } else { + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00a70600 + } + + //cs42l83_headset_amp_setup_TDM_proper_full(codec); + cs42l83_headset_amp_setup_TDM_proper(codec, full); + + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x02cf0700 + + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x000000c0); // 0x02c707c0 +// snd_hda: 44 ['AC_PINCTL_OUT_EN', 'AC_PINCTL_HP_EN'] + +} + +static void cs_8409_headset_amp_disable_TDM_proper(struct hda_codec *codec, int full) +{ + int retval; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + // AppleHDATDMBusManagerCS8409::disableTDMPath + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0029, 0x0000, 0x00000800, 0 ); // coef read 2411 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0029, 0x8800, 0x00000000, 0 ); // coef write 2415 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x002a, 0x0000, 0x00000820, 0 ); // coef read 2419 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x002a, 0x8820, 0x00000000, 0 ); // coef write 2423 + + if (full) + { + // AppleHDATDMBusManagerCS8409::disableTDMPath + // snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0001, 0xffff, 0x0000a801, 0, 2185 ); // coef write mask 2185 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0xa800, 0x0000a801, 0x0001, 0 ); // coef write mask 2185 + + // AppleHDATDMBusManagerCS8409::disableTDMPath + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000240, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef read 2191 + + new_coef1 = (ret_coef1 & 0xffbf); // clear our 0x40 bit + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0200, 0x00000000, 2195 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 2195 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 2195 + } + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 2427 ); // coef read 2427 + + tdm_in_use(codec, 301); + + if (full) + { + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x00000000, 0 ); // coef write 2452 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00000000); + } + else + { + + // snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 2624 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 2628 + + new_coef71 = (ret_coef71 & 0xffff); // why doesnt this really disable this here?? + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_TDM_proper coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 2632 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 2635 + + } + +} + +static void cs_8409_headset_amp_format_setup_disable(struct hda_codec *codec, int full) +{ + int retval; + + mycodec_info(codec, "cs_8409_headset_amp_format_setup_disable full %d\n", full); + + cs_8409_headset_amp_disable_TDM_proper(codec, full); + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x00a70600 +// snd_hda: conv stream channel map 10 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x0a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x00a20000 +// snd_hda: stream format 10 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + retval = snd_hda_codec_read_check(codec, 0x2c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000040, 0); // 0x02cf0700 + + snd_hda_codec_write(codec, 0x2c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x02c70700 +// snd_hda: 44 [] + +} + + +static void cs42l83_headset_mike_format_setup_enable(struct hda_codec *codec, int nullformat, int full) +{ + int retval = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 1)] + + if (nullformat) + { + // note that 0x4031 is Apples fixed format - but this is for plugin stage when we have + // not defined any format yet so just use it - we overwrite below when actually play + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 + if (full) + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 + } + else + { + if (full) + cs_8409_really_update_stream_format(codec, 0x1a, 1, 2, 0); + else + cs_8409_really_update_stream_format(codec, 0x1a, 1, 0, 0); + } + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000cccc, 0, 0 ); // coef write mask 12272 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000000, 0, 0 ); // coef write mask 12278 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008000, 0 ); // coef read 12285 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 12289 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008000, 0 ); // coef read 12293 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 12297 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 12301 ); // coef read 12301 + + tdm_in_use(codec, 201); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 12370 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 12374 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_mike_format_setup_enable coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 12378 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 12381 + + if (full) + { + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 + // snd_hda: 60 ['AC_PINCTL_IN_EN'] + } + +} + +static void cs42l83_headset_mike_format_setup_disable(struct hda_codec *codec) +{ + int retval = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + +// snd_hda: # AppleHDATDMBusManagerCS8409::disableTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 13141 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x8800, 0x00000000, 0 ); // coef write 13145 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 13149 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x8820, 0x00000000, 0 ); // coef write 13153 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13157 ); // coef read 13157 + + tdm_in_use(codec, 202); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13226 + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13230 + + new_coef71 = (ret_coef71 & 0xffff); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_mike_format_setup_disable coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 13234 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13234 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13234 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13237 + + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x01a70600 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x01a20000 +// snd_hda: stream format 26 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + +} + +// NOTE - the following routines NOT fixed up yet - currently not used!! + +static void cs42l83_headset_mike_format_setup_enable1(struct hda_codec *codec) +{ + //int retval = 0; + + //snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + cs_8409_really_update_stream_format(codec, 0x1a, 1, 0, 0); + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13249 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13255 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008800, 0 ); // coef read 13262 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13266 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008820, 0 ); // coef read 13270 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13274 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13278 ); // coef read 13278 + + tdm_in_use(codec, 203); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13347 + + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13351 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13355 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13358 + +} + +static void cs42l83_headset_mike_format_setup_enable1a(struct hda_codec *codec) +{ + //int retval = 0; + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13249 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13255 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00008800, 0 ); // coef read 13262 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13266 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00008820, 0 ); // coef read 13270 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13274 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13278 ); // coef read 13278 + + tdm_in_use(codec, 203); + +} + +static void cs42l83_headset_mike_format_setup_enable1b(struct hda_codec *codec) +{ + int retval = 0; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004031); // 0x01a24031 +// snd_hda: stream format 26 [('CHAN', 2), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000010); // 0x01a70610 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 1)] + + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0012, 0xaccc, 0xffff, 0x0000accc, 0, 0 ); // coef write mask 13368 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0011, 0x0001, 0xffff, 0x00000001, 0, 0 ); // coef write mask 13374 + +// snd_hda: # AppleHDATDMBusManagerCS8409::setupTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 13381 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x0800, 0x00000000, 0 ); // coef write 13385 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 13389 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x0820, 0x00000000, 0 ); // coef write 13393 + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00008800, 13397 ); // coef read 13397 + + tdm_in_use(codec, 204); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13466 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000800f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 13470 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0x800f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 13474 + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 13477 + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + +} + +// NOTE - end of unfixed routines + + +static void cs42l83_headset_amp_disable_and_mike_disable_TDM_proper(struct hda_codec *codec) +{ + //int retval = 0; + int ret_coef1 = 0; + int new_coef1 = 0; + int ret_coef71 = 0; + int new_coef71 = 0; + +// snd_hda: # AppleHDATDMBusManagerCS8409::disableTDMPath: + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0049, 0x0000, 0x00000800, 0 ); // coef read 10459 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0049, 0x8800, 0x00000000, 0 ); // coef write 10463 + snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x004a, 0x0000, 0x00000820, 0 ); // coef read 10467 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x004a, 0x8820, 0x00000000, 0 ); // coef write 10471 + + // this section is actually disabling the headset amp TDM setup + +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x5400, 0xffff, 0x0000fc00, 0, 10475 ); // coef write mask 10475 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0082, 0x0000, 0xa800, 0x0000fc00, 0x5400, 0 ); // coef write mask 10475 + + ret_coef1 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0001, 0x0000, 0x00000260, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef read 10481 + + new_coef1 = (ret_coef1 & 0xffbf); + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_and_mike_disable_TDM_proper coef 0x01 update 0x%04x 0x%04x \n", ret_coef1, new_coef1); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, 0x0220, 0x00000000, 10485 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 10485 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0001, new_coef1, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::disableTDMPath coef write 10485 + + // end of section disabling the headset amp TDM setup + + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: AppleHDATDMBusManagerCS8409::tdmInUse: + //snd_hda_coef_item(codec, 0, CS8409_VENDOR_NID, 0x0019, 0x0000, 0x00000800, 10489 ); // coef read 10489 + + tdm_in_use(codec, 401); + +// snd_hda: # AppleHDATDMBusManagerCS8409::configureTDMUR: + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x006c, 0x001f, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10494 + + + ret_coef71 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0071, 0x0000, 0x0000c00f, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef read 10498 + + new_coef71 = (ret_coef71 & 0xffff); // I dont get this - it doesnt seem to change this at all - but this is for output 0x800f (headphones) or 0x400f (amps) + myprintk_dbg("snd_hda_intel: masked cs42l83_headset_amp_disable_and_mike_disable_TDM_proper coef 0x71 update 0x%04x 0x%04x \n", ret_coef71, new_coef71); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, 0xc00f, 0x00000000, 10502 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10502 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0071, new_coef71, 0x00000000, 0 ); // AppleHDATDMBusManagerCS8409::configureTDMUR coef write 10502 + + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, 0x7f0, 0x00b6 ); // AppleHDATDMBusManagerCS8409::configureTDMUR write verb 10505 + +} + +static void cs42l83_headset_amp_disable_and_mike_format_setup_disable(struct hda_codec *codec) +{ + int retval = 0; + + cs42l83_headset_amp_disable_and_mike_disable_TDM_proper(codec); + + // note this means the cached stream data in the hda_cvt_setup struct will now be inconsistent + // we need to ensure any further stream format re-update MUST be a forced update + // still not clear if should be calling eg __snd_hda_codec_cleanup_stream + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x01a70600 +// snd_hda: conv stream channel map 26 [('CHAN', 0), ('STREAMID', 0)] + + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_STREAM_FORMAT, 0x00000000); // 0x01a20000 +// snd_hda: stream format 26 [('CHAN', 1), ('RATE', 48000), ('BITS', 8), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 0); // 0x03cf0700 + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000000); // 0x03c70700 +// snd_hda: 60 [] + +} + + +static void cs_8409_linein_format_setup_enable(struct hda_codec *codec) +{ + int retval; + int ret_coef9 = 0; + int new_coef9 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + // theres some weird issue here + // index 0x0009 has bit 0x0080 set only after an unplug event with headset with mike + // but then never seems to be turned off!!! + + // 0x45 -> 0x23 is line input + + + // now updated to not write the Apple format but use my format setting routines + // (remember we have limited the allowed formats to acceptable ones) + // note that apparently we can set the format with the nid powered down but for setting the + // stream id the nid has to be powered up + // we may wish to ignore the power down here - because we reactivate the nid only a few steps + // later + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_STREAM_FORMAT, 0x00004033); // 0x02324033 +// snd_hda: stream format 35 [('CHAN', 4), ('RATE', 44100), ('BITS', 24), ('RATE_MUL', 1), ('RATE_DIV', 1)] + + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10555); // 0x023f0500 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02370500 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10558); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D0); + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000012); // 0x02370612 +// snd_hda: conv stream channel map 35 [('CHAN', 2), ('STREAMID', 1)] + + + // using the stored stream parameters update nid 0x23 stream parameters + // we have limited the allowed formats so should only have working formats here + //cs_8409_really_update_stream_format(codec, 0x23, 1, 1, 0); + cs_8409_really_update_stream_format(codec, spec->linein_amp_nid, 1, 1, 0); + + + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02370503 + //retval = snd_hda_codec_read_check(codec, 0x23, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10561); // 0x023f0500 + hda_set_node_power_state(codec, spec->linein_amp_nid, AC_PWRST_D3); + + +// snd_hda: # AppleHDAWidgetCS8409::setConnectionSelect: + ret_coef9 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x000000b3, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef read 10563 + //new_coef9 = ret_coef9 | 0x80; // I dont get this bit set - see above + new_coef9 = ret_coef9 | spec->reg9_linein_dmic_mo; // I dont get this bit set - see above + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_format_setup_enable coef 0x09 update 0x%04x 0x%04x \n", ret_coef9, new_coef9); + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0x00000000, 10567 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10567 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0009, new_coef9, 0x00000000, 0 ); // AppleHDAWidgetCS8409::setConnectionSelect coef write 10567 + //snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02370100 + snd_hda_codec_write(codec, spec->linein_amp_nid, 0, AC_VERB_SET_CONNECT_SEL, 0x00000000); // 0x02370100 + +} + +static void cs_8409_linein_stream_on_nid(struct hda_codec *codec) +{ + int retval; + int reg_coef82 = 0; + int new_coef82 = 0; + + struct cs8409_apple_spec *spec = codec->spec; + + reg_coef82 = snd_hda_coef_item_check(codec, 0, CS8409_VENDOR_NID, 0x0082, 0x0000, 0x00005401, 0 ); // coef read 10584 + + //new_coef82 = (reg_coef82 | 0x2); + new_coef82 = (reg_coef82 | spec->reg82_linein_dmic_scl); + myprintk_dbg("snd_hda_intel: masked cs_8409_linein_stream_on_nid coef 0x0082 update 0x%04x 0x%04x \n", reg_coef82, new_coef82); + + //snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, 0x5403, 0x00000000, 10588 ); // coef write 10588 + snd_hda_coef_item(codec, 1, CS8409_VENDOR_NID, 0x0082, new_coef82, 0x00000000, 10588 ); // coef write 10588 + + //retval = snd_hda_codec_read_check(codec, 0x45, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + retval = snd_hda_codec_read_check(codec, spec->linein_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000000, 0); // 0x045f0700 + + snd_hda_codec_write(codec, spec->linein_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04570720 + + //snd_hda_codec_write(codec, 0x45, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x04570720 +// snd_hda: 69 ['AC_PINCTL_IN_EN'] + +} + +static void cs_8409_intmike_stream_conn_off_disable(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + // more weird issue here + // index 0x0009 has bit 0x0100 set only after an unplug event with headset with mike + // it is reset + + // 0x44 -> 0x22 is internal (I think) mike input (macbook pro) + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10596); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10599); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10602); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this is NOT from setConnectionSelect - unknown where from + // very not clear what this does - it appears as part of the multiple disable/enables +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x01b3, 0xffff, 0x000000b3, 0, 10604 ); // coef write mask 10604 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0100, 0x0000, 0x000000b3, 0x01b3, 0 ); // coef write mask 10604 + +} + + +static void cs_8409_intmike_stream_conn_off_enable(struct hda_codec *codec) +{ + int retval; + + struct cs8409_apple_spec *spec = codec->spec; + + snd_hda_codec_write(codec, CS8409_VENDOR_NID, 0, AC_VERB_SET_PROC_STATE, 0x00000001); // 0x04770301 + + retval = snd_hda_codec_read_check(codec, spec->intmike_adc_nid, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_CONV, 0x00000000, 0x00000000, 0); // 0x022f0600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10676); // 0x022f0500 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x02270500 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000030, 10679); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D0); + + snd_hda_codec_write(codec, spec->intmike_adc_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_CHANNEL_STREAMID, 0x00000000); // 0x02270600 +// snd_hda: conv stream channel map 34 [('CHAN', 0), ('STREAMID', 0)] + + //snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_POWER_STATE, 0x00000003); // 0x02270503 + //retval = snd_hda_codec_read_check(codec, 0x22, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000033, 10682); // 0x022f0500 + hda_set_node_power_state(codec, spec->intmike_adc_nid, AC_PWRST_D3); + + // this is NOT from setConnectionSelect - unknown where from + // very not clear what this does - it appears as part of the multiple disable/enables +// snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x00b3, 0xffff, 0x000001b3, 0, 10684 ); // coef write mask 10684 + snd_hda_coef_item_masked(codec, 2, CS8409_VENDOR_NID, 0x0009, 0x0000, 0x0100, 0x000001b3, 0x00b3, 0 ); // coef write mask 10684 + +} + + +static void cs_8409_intmike_linein_resetup(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + + // for some very strange reason we setup a 4 channel format after unplug of headset with mike + + //cs_8409_intmike_format_setup_format33(codec); + + cs_8409_intmike_format_setup_enable(codec, 0x4033, 1); + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_stream_on_nid(codec); + + cs_8409_linein_format_setup_enable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_stream_on_nid(codec); + + + cs_8409_intmike_stream_conn_off_disable(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + + //cs_8409_intmike_format_setup_format_nouse(codec); + //cs_8409_really_update_stream_format(codec, 0x22, 1, 0, 0); + cs_8409_really_update_stream_format(codec, spec->intmike_adc_nid, 1, 0, 0); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + + + cs_8409_intmike_stream_conn_off_enable(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + + cs_8409_intmike_volume_mute(codec); + + cs_8409_linein_volume_mute(codec); + + cs_8409_intmike_volume_unmute(codec); + + cs_8409_linein_volume_unmute(codec); + + cs_8409_inputs_power_nids_off(codec); + +} + + +// this is where we need to decode the actions to be taken +// note that the button interrupts are undocumented for the cs42l42 (reserved) +// not yet clear which one is up and which one is down!! +// so after button detect 0x1b7b is 0x14 and 0x1b7c is 0x0a +// for 0x1b7b 0x14 are reserved bits for cs42l42 - but the 0x04 only seen on detection +// actual button presses are 0x01, 0x02 and button release 0x10 +// for 0x1b7c 0x02 is a short release for buttons, 0x08 is reserved +// the mask bits for 0x1b7a seem to be 0xe7 for buttons defining 0x08 as the button detect interrupt +// (0x1b79 is mask, 0x1b7b status; 0x1b7a is mask, 0x1b7c is presumed status, 0x131b is mask, 0x1308 status +// 0x1320 is mask, 0x130f status) +#define TIP_SENSE_PLUG 0x400000 +#define TIP_SENSE_UNPLUG 0x200000 +#define BUTTON_DOWN_PRESS 0x10000 +#define BUTTON_UP_PRESS 0x20000 +#define BUTTON_RELEASE 0x100000 +// pressing the play/pause button on earbuds yields 0x100 on down and 0x200 on up +#define BUTTON_TOGGLE_DOWN_PRESS 0x100 +#define BUTTON_TOGGLE_UP_PRESS 0x200 +#define BUTTON_DETECT_MAIN 0x1800 // we only see 0x800 but the mask allows for these 2 bits +#define BUTTON_DETECT_MASK 0x60 +#define BUTTON_DETECT1 0x40 +#define BUTTON_DETECT2 0x20 +#define MIKE_CONNECT 0x02 +#define BUTTONS (BUTTON_UP_PRESS | BUTTON_DOWN_PRESS | BUTTON_TOGGLE_UP_PRESS | BUTTON_TOGGLE_DOWN_PRESS) +#define HSDET_AUTO_DONE 0x02 +#define PDN_DONE 0x01 + +static void cs_8409_headset_plugin_event(struct hda_codec *codec); +static void cs_8409_headset_unplug_event(struct hda_codec *codec); +static void cs_8409_headset_type_detect_event(struct hda_codec *codec); +static int cs_8409_set_power_state(struct hda_codec *codec, int power_state); +static void cs_8409_enable_headset_streaming(struct hda_codec *codec); +static void cs_8409_headset_button_detect_event(struct hda_codec *codec); +static void cs_8409_headset_button_event(struct hda_codec *codec, int buttons); +static void cs_8409_plugin_event_continued(struct hda_codec *codec); + + +static void cs_8409_interrupt_action(struct hda_codec *codec, int int_response) +{ + int retval; + int headset_state; + int update_jacks = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // so if Im analyzing the Dell code correctly + // I think we should only do the snd_hda_jack_report_sync after all jack detection + // plus all nid mute/unmute and widget output/input enable + // because the Dell code first does tip sense, then (if wanted) jack type detection + // then runs snd_hda_jack_unsol_event which does any callbacks + // and finally the snd_hda_jack_report_sync + + if ((int_response & TIP_SENSE_PLUG) == TIP_SENSE_PLUG) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - plug in\n"); + cs_8409_headset_plugin_event(codec); + } + else if ((int_response & TIP_SENSE_UNPLUG) == TIP_SENSE_UNPLUG) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug\n"); + cs_8409_cs42l83_mark_jack(codec); + cs_8409_headset_unplug_event(codec); + // so although this more consistent with linux way (all automute etc callbacks done before report sync) + // it seems we need to update the linux user side before doing the amp reset when playing + // with cs_8409_cs42l83_jack_report_sync here there is a few 10s milliseconds period where get anomalous volume + // because we start playing through the amps while linux user side still says its headphone output + // - this may be because updating the linux user side takes a little while + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + } + else if ((int_response & HSDET_AUTO_DONE) == HSDET_AUTO_DONE) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - headset detected\n"); + + cs_8409_headset_type_detect_event(codec); + + // and this is where life gets really complicated + // if we have a mike we do a button detect - but that leads to an unsolicited response + // so we only continue here I think if we dont have a mike + if (!(spec->have_mike)) { + cs_8409_cs42l83_mark_jack(codec); + cs_8409_plugin_event_continued(codec); + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - headset detect no mike jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + } + } + // not clear what test is here - but this should check what we see - one button interrupt seems to be activated + // when doing the button detect + // not sure what the exact button interrupt is - we get 0x140800 + // so the button detect interrupt is 0x0800 - the 0x140000 are actual button interrupts (undocumented for cs42l42) + else if (int_response & BUTTON_DETECT_MAIN) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - buttons detected\n"); + cs_8409_headset_button_detect_event(codec); + + cs_8409_cs42l83_mark_jack(codec); + + cs_8409_plugin_event_continued(codec); + + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button detect jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + + } + else if (((int_response & BUTTON_UP_PRESS) == BUTTON_UP_PRESS) || + ((int_response & BUTTON_DOWN_PRESS) == BUTTON_DOWN_PRESS) || + ((int_response & BUTTON_TOGGLE_UP_PRESS) == BUTTON_TOGGLE_UP_PRESS) || + ((int_response & BUTTON_TOGGLE_DOWN_PRESS) == BUTTON_TOGGLE_DOWN_PRESS)) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button event on \n"); + cs_8409_headset_button_event(codec, int_response); + update_jacks = 1; + + } + else if (((int_response & BUTTON_RELEASE) == BUTTON_RELEASE)) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - button event off \n"); + //cs_8409_cs42l83_mark_jack(codec); + cs_8409_headset_button_event(codec, int_response); + update_jacks = 1; + + } + else if ((int_response & PDN_DONE) == PDN_DONE) + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - power down\n"); + } + else + { + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - UNKNOWN INTERRUPT 0x%08x\n", int_response); + } + + return; +} + + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec); +static void cs_8409_headset_streaming_preplay(struct hda_codec *codec); +static void cs_8409_intmike_linein_disable(struct hda_codec *codec); +static void cs_8409_headset_mike_streaming_preplay(struct hda_codec *codec, int nullformat); +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec); + + +static void cs_8409_plugin_event_continued(struct hda_codec *codec) +{ + struct cs8409_apple_spec *spec = codec->spec; + int headset_plugged_in = 0; + int retval = 0; + + mycodec_info(codec, "cs_8409_plugin_event_continued start\n"); + + // 0 is power state - 0 is powered on +ve powered/powering down + // headset_plugged_in indicates if headset still plugged in or not + headset_plugged_in = cs_8409_set_power_state(codec, 0); + + mycodec_info(codec, "cs_8409_plugin_event_continued headset_plugged_in %d\n", headset_plugged_in); + + if (headset_plugged_in) + { + // now handle plugin while playing + if (spec->playing) + { + + cs_8409_amps_disable_streaming(codec); + + + cs_8409_enable_headset_streaming(codec); + + + // the following is just cs_8409_enable_headset_streaming + //cs43l83_headset_amp_format_setup(codec); + //mycodec_info(codec, "cs42l83_headset_play_setup_on\n"); + //cs42l83_headset_play_setup_on(codec); + // power on audio output + //cs42l83_set_power_state_on(codec, 0); + //cs42l83_headset_enable_on(codec); + + + // so OSX now does another one of its enable off/enable on - ignoring + + //cs_8409_headset_enable_off(codec); + //cs42l83_power_off_codec_output_off(codec); + //headphone_play_stop_buffers_off(codec); + //headphone_play_stop_power_off(codec); + //headphone_play_stop_headphone_format_setup_disable(codec); + + //cs_8409_enable_headset_streaming(codec); + + // we need to reset formats here - so we follow the same path as a simple amp + // or headphone play ie after the pre-prepare we force a reset of the + // of the stream format + + // using explicit nid here!! + //cs_8409_reset_stream_format(codec, 0xa, 1, 1); + + } + else + { + + // try removing this - we still do a partial setup when actually play on OSX + // and if we stop play then do another play we do a full setup + // - why not just enable when we play?? + // in any case we initially made this a full setup and it worked + //cs_8409_headset_streaming_preplay(codec); + + // so now think on OSX we pre-setup the headphone and mike here + // when we dont know if we will be playing or capturing + // in particular realised that cs42l83_configure_serial_port is only called + // for the headphone setup - but it sets both primary ASP (Audio Serial Port) + // transmit and receive frequencies - which would seem to be important for + // capturing!! + + //spec->headset_presetup_done = 1; + + } + + // NOTA BENE - no concept/implementation of plugging in while capturing!! + + mycodec_info(codec, "cs_8409_plugin_event_continued headset_phase %d\n", spec->headset_phase); + + // this event now gets called if boot with headset plugged in + // but from this point the boot phase setup is different + // - now a headset_phase of 1 indicates booted with headset plugged in + // - headset phase of 2 or more means post boot headset plugin + if (spec->headset_phase >= 2) + { + + // ensure the intmike/linein nids are powered off + cs_8409_inputs_power_nids_off(codec); + + + retval = cs42l83_headphone_sense(codec); + + mycodec_info(codec, "cs_8409_plugin_event_continued headphone sense 0x%x\n", retval); + + if (!(retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_plugin_event_continued JACK DISCONNECT UNIMPLEMENTED!!\n"); + } + + if (spec->have_mike) + { + if (spec->capturing) + { + dev_info(hda_codec_dev(codec), "cs_8409_plugin_event_continued PLUGIN WHILE CAPTURING UNIMPLEMENTED!!\n"); + } + + // this is just calling this routine + //cs_8409_headset_mike_setup_nouse + + cs_8409_intmike_linein_disable(codec); + + // is this a good position to switch the inputs?? + switch_input_src(codec); + + // confirmed that if do a second recording we get a full setup as for playing above + // - so why not just enable when we capture?? + // (only plausible reason so far is to reduce setup time because of the long time + // to send the i2c commands??) + // NOTE - this is complicated because on OSX it appears the headphone setup is always + // done - even if just capturing + // going with OSX way and doing the headphone setup as well + + // so this is a problem - at this point we dont have a stream + // so our format is null + // what to do?? + // now moving all this setup into the actual capture setup + // - as did with the play setup + + //cs_8409_headset_mike_streaming_preplay(codec, 1); + + //spec->headset_presetup_done = 1; + + + cs_8409_headset_mike_buttons_enable(codec); + + } + } + + } + else + {} // headset unplugged - should be handled by the unplug interrupt + + mycodec_info(codec, "cs_8409_plugin_event_continued end\n"); +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec); +static void cs_8409_plugin_complete_detect(struct hda_codec *codec); + + +static void cs_8409_headset_plugin_event(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_plugin_event pre plugin handle_detect\n"); + + cs_8409_plugin_handle_detect(codec); + + mycodec_info(codec, "cs_8409_headset_plugin_event post plugin handle_detect\n"); + + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 1066); // 0x001f0500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // this seems to be here but no idea where coming from + retval = read_gpio_status_check(codec); + + + // following code likely from AppleHDAMikeyInternalCS8409::handleJackDetectUR + // moved from cs_8409_plugin_handle_detect + + // then call setTimer to initiate function after a time period + + // this is NOT a debug sleep - it occurs on all plugin events on OSX for some reason + msleep(1800); + + //retval = snd_hda_codec_read_check(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0x00000000, 0x00000000, 1069); // 0x001f0500 + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + + mycodec_info(codec, "cs_8409_headset_plugin_event pre plugin complete_detect\n"); + + cs_8409_plugin_complete_detect(codec); + + mycodec_info(codec, "cs_8409_headset_plugin_event post plugin complete_detect\n"); + + spec->headset_phase = 3; + +} + +static void cs_8409_plugin_handle_detect(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // now think this is AppleHDAMikeyInternalCS8409::handleJackDetectUR + + // which calls: + // AppleHDAMikeyInternalCS8409::disableButtonDetection + // AppleHDAMikeyInternalCS8409::enableHPClamps + + // AppleHDAMikeyInternalCS8409::enableHPClamps calls: + // AppleHDATDM_Codec::setHPOutClamp + + mycodec_info(codec, "cs_8409_plugin_handle_detect start\n"); + + // this is a pre-value - indicates we have had a jack detect + // but set to 1 when have checked with cs42l83_headphone_sense for headset + // at the moment not used + spec->jack_present = 2; + + cs42l83_headset_button_detect_interrupts_off(codec); + + cs42l83_headset_set_hpout_clamp_disable(codec); + + // IOSleep(1) here + msleep(1); + + mycodec_info(codec, "cs_8409_plugin_handle_detect end\n"); +} + +static void cs_8409_plugin_complete_detect(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + // so AppleHDAMikeyInternalCS8409::generalTimerCallback calls AppleHDAMikeyInternalCS8409::completeJackDetectUR + // AppleHDAMikeyInternalCS8409::generalTimerCallback is set as a timer callback in the AppleHDAMikeyInternal::init + // using the IOTimerEventSource::timerEventSource function + + // this is AppleHDAMikeyInternalCS8409::completeJackDetectUR + // its first call is to AppleHDATDM_Codec::getHeadphonePinSense + // which if returns 0 in the bool arg jumps to AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // - which does the disconnect + + mycodec_info(codec, "cs_8409_plugin_complete_detect start\n"); + + // so thats weird - the first call is a power call which doesnt seem to exist in the log + //hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + retval = cs42l83_headphone_sense(codec); + + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone sense 0x%x\n", retval); + + if ((retval & 0x80)) + { + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone sensed\n"); + + spec->jack_present = 1; + spec->headset_enable = 1; + + cs42l83_complete_jack_detect(codec); + + cs42l83_power_hs_bias_on(codec); + + // this seems to be setting an interrupt on 0x131b for headset detect + // - but there doesnt seem to be a delay anywhere here + // so it must be immediately triggered + + cs42l83_enable_hs_auto_int_on(codec); + + + cs42l83_unplug_interrupt_setup(codec); + + + cs42l83_set_hpout_pulldown_off(codec); + + cs42l83_headset_detect_on(codec); + //cs42l83_headset_detect_onoff(codec, 1); + + } + else + { + mycodec_info(codec, "cs_8409_plugin_complete_detect headphone NOT sensed\n"); + + spec->jack_present = 0; + spec->headset_enable = 0; + + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + dev_info(hda_codec_dev(codec), "cs_8409_plugin_complete_detect no headphone UNIMPLEMENTED!!\n"); + + } + + mycodec_info(codec, "cs_8409_plugin_complete_detect end\n"); +} + + + +static void cs_8409_headset_type_detect_event(struct hda_codec *codec) +{ + int retval; + int retstate; + int flag = 1; + int headset_type = 0; + struct cs8409_apple_spec *spec = codec->spec; + + // this is AppleHDAMikeyInternalCS8409::handleTypeDetectUR + // dont yet see the path to call this - Im guessing from some messaging call + + mycodec_info(codec, "cs_8409_headset_type_detect_event start\n"); + + // I think we get to here if have either an unplug event or headset detect event + // - in both cases we need to turn off the headset detect interrupt + // and unset for headset detect + + //plugin_enable_hs_auto_int_off(codec); + cs42l83_enable_hs_auto_int_off(codec); + + //headset_type = plugin_headset_type(codec); + headset_type = cs42l83_headset_type(codec); + + // we then have options based on the headset type + headset_type = headset_type & 0x3; + + // types are 0x00 (1), 0x01 (2), 0x02 (3) and 0x3 (4) + // type 1 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Mic + // type 2 Pin 1 Left, Pin 2 Right, Pin 3 Mic, Pin 4 Gnd + // type 3 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Gnd + // type 4 Optical!! + + mycodec_info(codec, "cs_8409_headset_type_detect_event headset type %d\n", headset_type); + + if (headset_type == 0x00 && flag == 0) + { + // 0x74df1 + + // if ?? goto 0x74ee2 + + // if ?? goto 0x751e8 + + // 0x74e42 + + //plugin_headset_detect_off(codec); + + // IOSleep(250); + + // possible AppleHDAMikeyInternalCS8409::handleJackDetectUR + + // goto 0x7546d + // IOLog() + // goto 0x75198 + // return + + // else + + // 0x751e8 + + //retval = plugin_headphone_sense1(codec); + + // if dont sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + //if (!(retval & 0x80)) + //{ + // // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // goto 0x74ee2 + //} + + // error!! + } + + if (headset_type == 0x00) + { + // 0x74df1 + // goto 0x74ee2 + spec->headset_type = 1; + spec->have_mike = 1; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset has mike!!\n"); + } + else if (headset_type == 0x01) + { + // 0x74e98 + // insert Mikey event 0xfe + // goto 0x74ee2 + spec->headset_type = 2; + spec->have_mike = 1; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset has mike!!\n"); + } + else if (headset_type == 0x02) + { + // 0x74eb9 + // goto 0x74ee2 + spec->headset_type = 3; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset does not have mike!!\n"); + } + else if (headset_type == 0x03) + { + // this is SPDIF!! + // 0x74ec3 + // insert Mikey event 0xfc + spec->headset_type = 4; + dev_info(hda_codec_dev(codec), "cs_8409_headset_type_detect_event headset does not have mike!!\n"); + } + + // 0x74ee2 + + cs42l83_headset_detect_off(codec); + //cs42l83_headset_detect_onoff(codec, 0); + + cs42l83_set_hpout_pulldown_on(codec); + + // IOSleep(1); + msleep(1); + + // 0x74ff1 + + cs42l83_set_hpout_clamp_enable(codec); + + // IOSleep(1); + msleep(1); + + + if (spec->have_mike) + { + + cs42l83_enable_hsbias_auto_clamp_on(codec); + + //cs42l83_enable_hsbias_auto_clamp_off(codec); + cs42l83_enable_hsbias_auto_clamp_off0(codec); + + // I dont see a difference in these 2 functions + //cs42l83_power_hs_bias_off1(codec); + cs42l83_power_hs_bias_off(codec); + + // difference from no mike headphones + + cs42l83_setup_button_detect(codec); + + cs42l83_power_hs_bias_button_on(codec); + + cs42l83_enable_hsbias_auto_clamp_off1(codec); + + } + else + { + // goto 0x75a02 + + // 0x750a2 + + cs42l83_headset_mike_detect_off(codec); + + cs42l83_power_hs_bias_off(codec); + } + + + // 0x75149 + + // there is a call to dispatchStatelessTagToEngines which is likely what initiates the stream setup etc + + + // 0x7546d + + // 0x75198 + + // there is an unknown call here - possible setPowerState + // cannot figure out if this is doing anything - none of the functions seem to fit the log + // so now think this function isnt really doing anything + // and the setPowerState is from some other function call path ie the result + // of dispatchStatelessTagToEngines + + + // exit routine after this + + mycodec_info(codec, "cs_8409_headset_type_detect_event end\n"); +} + + +static int cs_8409_set_power_state(struct hda_codec *codec, int power_state) +{ + int retval = 0; + int retstate = 0; + int flag = 0; + + mycodec_info(codec, "cs_8409_set_power_state start\n"); + + // this is likely AppleHDAMikeyInternalCS8409::setPowerState as there is + // a pin sense and handleJackDisconnectUR in AppleHDAMikeyInternalCS8409::setPowerState + // in fact all AppleHDAMikeyInternalCS8409::setPowerState does is check the headphone sense + // and then either do a handleJackDisconnectUR or handleJackDetectUR + + if (flag) + { + // check for headphone whatever power state of HDA is + + retval = cs42l83_headphone_sense(codec); + + // if sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDetectUR + + if ((retval & 0x80)) + { + // AppleHDAMikeyInternalCS8409::handleJackDetectUR + dev_info(hda_codec_dev(codec), "cs_8409_set_power_state JACK DETECT UNIMPLEMENTED!!\n"); + } + + retstate = 0; + } + else + { + // only check for headphone if HDA powered up + if (power_state == 0) + { + retval = cs42l83_headphone_sense(codec); + + // if dont sense headphone guess we do AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + if (!(retval & 0x80)) + { + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + dev_info(hda_codec_dev(codec), "cs_8409_set_power_state JACK DISCONNECT UNIMPLEMENTED!!\n"); + retstate = 0; + } + else + retstate = 1; + } + } + + mycodec_info(codec, "cs_8409_set_power_state end\n"); + + return retstate; +} + + +static void cs_8409_headset_button_detect_event(struct hda_codec *codec) +{ + int ret_button; + int ret_mike; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_button_detect_event start\n"); + + // this returns significant state - headphone sense (shift 16), and 2 reads from register 0x1b78 (second one shifted 8) + ret_button = cs42l83_handle_button_detect(codec); + + mycodec_info(codec, "cs_8409_headset_button_detect_event button data 0x%08x\n", ret_button); + + // so now seen on imacs we have a button detect of 0x20 rather than 0x40 previously seen + // - this maybe an Apple headset/non-Apple headset issue rather than imac issue (the headset was non-Apple) + //if ((ret_button & BUTTON_DETECT) == BUTTON_DETECT) + if (ret_button & BUTTON_DETECT_MASK) + { + mycodec_info(codec, "cs_8409_headset_button_detect_event HAVE BUTTON\n"); + spec->have_buttons = 1; + } + + // this is a read from same register 0x1b78 - which seems to contain both senses + // - button sense 0x40/0x20 (assumed) and mike sense 0x02 - known but undocumented + // do we do anything with this?? + // we have aleady set have_mike prior to this + // could log an error here + ret_mike = cs42l83_mike_connected(codec); + + mycodec_info(codec, "cs_8409_headset_button_detect_event mike data 0x%08x\n", ret_mike); + + if ((ret_mike & MIKE_CONNECT) != MIKE_CONNECT) + dev_err(hda_codec_dev(codec), "ERROR - has mike but mike not connected - not analyzed!!\n"); + + + mycodec_info(codec, "cs_8409_headset_button_detect_event end\n"); +} + +static void cs_8409_headset_button_event(struct hda_codec *codec, int buttons) +{ + mycodec_info(codec, "cs_8409_headset_button_event buttons 0x%08x\n", buttons); +} + + +static void cs_8409_intmike_linein_disable(struct hda_codec *codec) +{ + + cs_8409_intmike_stream_conn_off(codec); + + cs_8409_linein_stream_conn_off(codec); + + cs_8409_intmike_stream_off_nid(codec); + + cs_8409_linein_stream_off_nid(codec); + + cs_8409_intmike_volume_setup(codec, 0x27); + + cs_8409_intmike_format_setup_disable(codec); + + cs_8409_linein_volume_setup(codec, 0x27); + + cs_8409_linein_format_setup_disable(codec); + +} + +static void cs_8409_headset_mike_streaming_preplay(struct hda_codec *codec, int nullformat) +{ + + // confirmed that if do a second recording we get a full setup as for playing + // - so why not just enable when we capture?? + // (only plausible reason so far is to reduce setup time because of the long time + // to send the i2c commands??) + // NOTE - this is complicated because on OSX it appears the headphone setup is always + // done - even if just capturing + // going with OSX way and doing the headphone setup as well + + //cs42l83_headset_mike_format_setup_enable(codec, nullformat, 1); + + //cs42l83_input_set_output_sample_rate(codec); + + //cs42l83_mike_setup_audio_input(codec); + + //cs42l83_mike_enable(codec); + + + // yet again the usual disable/second enable + + //cs42l83_mike_disable(codec); + + //cs42l83_headset_mike_format_setup_disable(codec); + + + // cs42l83_headset_mike_format_setup_enable1(codec); + + // cs42l83_headset_mike_format_setup_enable1a(codec); + + // cs42l83_headset_mike_format_setup_enable1b(codec); + + // cs42l83_input_set_output_sample_rate1(codec); + + // cs42l83_input_setup_audio_input(codec); + + // cs42l83_mike_enable(codec); + + + //cs42l83_headset_mike_pin_enable(codec); + +} + +static void cs_8409_headset_mike_buttons_enable(struct hda_codec *codec) +{ + + // part of AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + cs42l83_configure_headset_button_interrupts(codec); + + cs42l83_enable_hsbias_auto_clamp_off2(codec); + + // following coded explicitly in handleButtonDetectUR + + cs42l83_hsbias_sense_on(codec); + +} + +static void cs_8409_headset_plugin_preplay_unused1(struct hda_codec *codec) +{ + + // these occur for headset with mike but seem to be triggered by + // by something - maybe volume control?? + // dont know where coming from - ignoring for now + + //plugin3_set_power_state_on(codec); + + //plugin3_headphone_play_start_headset_enable_on(codec); + + + //plugin3_headphone_sense3(codec); + + //plugin3_headphone_sense4(codec); + + + //plugin3_headphone_format_setup_enable2(codec); + +} + +static void cs_8409_headset_plugin_preplay_unused2(struct hda_codec *codec) +{ + // these occur for headset with mike but seem to be triggered by + // by something - this is definitely after an interrupt - but + // no interrupt triggered + // dont know where coming from - ignoring for now + + //plugin3_headset_enable_off(codec); + + //plugin3_power_off_codec_output_off(codec); + + //plugin3_buffers_off1(codec); + + //plugin3_headphone_format_setup_disable1(codec); + + //plugin3_headset_enable_off1(codec); + + //plugin3_power_off_codec_output_off1(codec); + +} + + +static void cs_8409_unplug_handle_disconnect(struct hda_codec *codec); + +static void cs_8409_headset_unplug_event(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headset_unplug_event start\n"); + + // Im guessing we are ensuring headphone is unplugged here + // what to do if not!! + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_info(hda_codec_dev(codec), "cs_8409_headset_unplug_event headphone still sensed - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_headset_unplug_event headphone not sensed - OK\n"); + + cs_8409_unplug_handle_disconnect(codec); + } + + mycodec_info(codec, "cs_8409_headset_unplug_event end\n"); +} + +static void cs_8409_unplug_handle_disconnect(struct hda_codec *codec) +{ + int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect start\n"); + + + cs42l83_plugin_interrupt_setup(codec); + + cs42l83_enable_hs_auto_int_off(codec); + + cs42l83_headset_detect2_off(codec); + + + if (spec->have_mike) + { + cs42l83_power_hs_bias_off(codec); + + cs42l83_enable_hsbias_auto_clamp_off3(codec); + + cs42l83_disable_button_interrupts(codec); + } + + + // Im guessing we are ensuring headphone is unplugged here + // what to do if not!! + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_err(hda_codec_dev(codec), "cs_8409_unplug_handle_disconnect headphone still sensed - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headphone not sensed - OK\n"); + + // even here this still has audio glitch + // - but with 100 ms wait later seems to fix it + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + spec->jack_present = 0; + + // try setting ALL jacks dirty - likely not needed + //snd_hda_jack_set_dirty_all(codec); + + dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + cs_8409_cs42l83_jack_report_sync(codec); + + + if (spec->playing) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 1 start\n"); + + cs42l83_headset_enable_off(codec); + + cs42l83_power_off_codec_output(codec); + + //unplug23_buffers_off(codec); + cs42l83_buffers_onoff(codec, 0); + + if ((spec->have_mike)) + cs_8409_headset_amp_format_setup_disable(codec, 0); + else + { + cs42l83_power_onoff(codec, 0); + + cs_8409_headset_amp_format_setup_disable(codec, 1); + } + + + cs42l83_headset_enable_off(codec); + + //unplug23_power_off_codec_output1(codec); + cs42l83_power_off_codec_output(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 1 end\n"); + } + + // so we have determined the volume/glitch issues are after this + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 1\n"); + //msleep(10000); + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 1 end\n"); + + // with previous jack_report_sync and this wait dont have a glitch + msleep(100); + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + //spec->jack_present = 0; + + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + + + // add a wait for user side update - still get a small glitch + //msleep(100); + + + // this is done if playing or not?? + // - changing - only setup amps if still playing + + if (spec->playing) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 2 start\n"); + + //unplug23_play_setup_TDM_6462(struct hda_codec *codec) + + //play_setup_TDM_amps12_debug(codec, 1); + play_setup_TDM_amps12(codec, 1); + + //unplug23_setup_amps_6462(struct hda_codec *codec) + + play_setup_amps12(codec); + + //unplug23_play_setup_TDM_7472(struct hda_codec *codec) + + play_setup_TDM_amps34(codec); + + //unplug23_play_setup_amps_7472(struct hda_codec *codec) + + play_setup_amps34(codec); + + //unplug23_sync_converters_on(struct hda_codec *codec) + + play_sync_converters_on(codec); + + + // so here linux user side reports still headphone output (because originally had not yet done + // jack_report_sync) which leads to audio glitch with output now through speakers + // so we need to update linux user side after headphone output disable above + // (the volume mismatch previously heard was due to incorrect handling of nid 0x03 update for stereo (ie 2 channel) source) + // can only think its delay from jack_report_sync till linux user side updated + // (we dont really have a massive lot of commands from here till jack_report_sync + // (here would be more consistent with linux way which does all power/automute/automic etc callbacks before jack_report_sync) + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 2\n"); + //msleep(10000); + //mycodec_info(codec, "cs_8409_unplug_handle_disconnect sleep 2 end\n"); + + + // silly me - we must update this here so jack_detect_update in jack_report_sync will determine the headset has been unplugged + //spec->jack_present = 0; + + //dev_info(hda_codec_dev(codec), "cs_8409_interrupt_action - unplug jack_report_sync\n"); + //cs_8409_cs42l83_jack_report_sync(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect playing 2 end\n"); + } + + // more duplicated disable/enables + + //unplug_sync_converters_off(codec); + //unplug_disable_amps_6462(codec); + //unplug_disable_TDM_6462(codec); + //unplug_disable_amps_7472(codec); + //unplug_disable_TDM_7472(codec); + + //unplug_setup_TDM2_6462(codec); + //unplug_setup_amps1_6462(codec); + //unplug_setup_TDM2_7472(codec); + //unplug_setup_amps1_7472(codec); + //unplug_sync_converters2_on(codec); + + + if (!(spec->have_mike)) + { + cs_8409_inputs_power_nids_off(codec); + } + + // and another headphone sense + + retval = cs42l83_headphone_sense(codec); + + if ((retval & 0x80)) + { + dev_err(hda_codec_dev(codec), "cs_8409_unplug_handle_disconnect headphone sensed again - NOT HANDLED - UNIMPLEMENTED!!!\n"); + } + else + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset not sensed - OK\n"); + + + if (spec->have_mike) + { + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset have mike start\n"); + + //unplug23_mike_disable(codec); + + cs42l83_mike_disable(codec); + + //unplug23_power_on_off(codec); + + cs42l83_power_onoff(codec, 0); + + //unplug23_headset_amp_disable_and_mike_format_setup_disable(codec); + + cs42l83_headset_amp_disable_and_mike_format_setup_disable(codec); + + // is this a good position to switch the inputs?? + switch_input_src(codec); + + //unplug23_intmike_linein_resetup(codec); + + cs_8409_intmike_linein_resetup(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset have mike end\n"); + } + + if (spec->playing) + { + if (!(spec->have_mike)) + { + cs_8409_inputs_power_nids_off(codec); + } + } + + if (spec->have_mike) + cs_8409_inputs_power_nids_off(codec); + + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable start\n"); + + cs42l83_unplug_headset_detect_off(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable 1\n"); + + cs42l83_headset_switch_control(codec); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset disable end\n"); + } + + } + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect headset about to reset vars\n"); + + // and reset all headset variables + + spec->jack_present = 0; + + spec->headset_type = 0; + + spec->have_mike = 0; + + spec->have_buttons = 0; + + spec->headset_play_format_setup_needed = 1; + spec->headset_capture_format_setup_needed = 1; + + spec->headset_presetup_done = 0; + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect jack_present %d\n", spec->jack_present); + + mycodec_info(codec, "cs_8409_unplug_handle_disconnect end\n"); +} + +static void cs42l83_headset_play_setup_on(struct hda_codec *codec) +{ + + // this is the function AppleHDATDM_CS42L83::enable for turning on headset for play + + + // the following section is often done pre-play + + cs42l83_configure_int_mclk(codec); + + //cs42l83_headset_power_on_on_nouse(codec); + cs42l83_power_onoff(codec, 1); + + cs42l83_configure_serial_port(codec); + + + // the following section always done before play + + cs42l83_output_set_input_sample_rate(codec); + + cs42l83_setup_audio_output(codec); + + // headset_setup_SPDIF_output(codec); - presumably if is SPDIF setup + + //cs42l83_headset_rcv_enable_on(codec); + cs42l83_buffers_onoff(codec, 1); + +} + + +static void cs42l83_headset_disable_nouse(struct hda_codec *codec, int full) +{ + myprintk("snd_hda_intel: cs42l83_headset_disable start\n"); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs42l83_power_off_codec_output end\n"); + + if (full) + { +#if 0 + // so this is old style - why didnt I update it?? + myprintk("snd_hda_intel: cs42l83_headset_rcv_enable_off start\n"); + + cs42l83_headset_rcv_enable_off(codec); + + myprintk("snd_hda_intel: cs42l83_headset_rcv_enable_off end\n"); + myprintk("snd_hda_intel: cs42l83_headset_power_off start\n"); + + cs42l83_headset_power_off(codec); + + myprintk("snd_hda_intel: cs42l83_headset_power_off end\n"); +#endif + myprintk("snd_hda_intel: cs42l83_buffers_onoff start\n"); + + //cs42l83_headset_rcv_enable_off(codec); + cs42l83_buffers_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff end\n"); + myprintk("snd_hda_intel: cs42l83_power_onoff start\n"); + + //cs42l83_headset_power_off(codec); + cs42l83_power_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_power_onoff end\n"); + } + + myprintk("snd_hda_intel: cs42l83_headset_disable end\n"); +} + +static void cs42l83_headset_disable(struct hda_codec *codec) +{ + myprintk("snd_hda_intel: cs42l83_headset_disable start\n"); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff start\n"); + + //cs42l83_headset_rcv_enable_off(codec); + cs42l83_buffers_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_buffers_onoff end\n"); + myprintk("snd_hda_intel: cs42l83_power_onoff start\n"); + + //cs42l83_headset_power_off(codec); + cs42l83_power_onoff(codec, 0); + + myprintk("snd_hda_intel: cs42l83_power_onoff end\n"); + + myprintk("snd_hda_intel: cs42l83_headset_disable end\n"); +} + + +static void cs_8409_headset_streaming_preplay(struct hda_codec *codec) +{ + //int retval; + + // this code performed on plugin even if not playing + // the usual enable/disable multiple times + + //plugin3_headphone_format_setup_enable(codec); + cs43l83_headset_amp_format_setup(codec, 1, 1); + + //plugin3_configure_int_mclk(codec); + cs42l83_configure_int_mclk(codec); + + //plugin3_power_on_on(codec); + cs42l83_power_onoff(codec, 1); + + //plugin3_configure_serial_port(codec); + cs42l83_configure_serial_port(codec); + + //plugin3_set_sample_rate(codec); + cs42l83_output_set_input_sample_rate(codec); + + //plugin3_setup_audio_output(codec); + cs42l83_setup_audio_output(codec); + + //plugin3_buffers_on(codec); + cs42l83_buffers_onoff(codec, 1); + + + // ignoring the disable + + //plugin3_buffers_off(codec); + //cs42l83_buffers_onoff(codec, 0); + + //plugin3_power_on_off(codec); + //cs42l83_headset_power_off(codec); + + //plugin3_headphone_format_setup_disable(codec); + //cs_8409_headset_amp_format_setup_disable(codec, 1); + + + //plugin3_headphone_format_setup_enable1(codec); + + //plugin3_headphone_format_setup_enable1a(codec); + + //plugin3_configure_int_mclk1(codec); + + //plugin3_power_on_on2(codec); + + //plugin3_configure_serial_port1(codec); + + //plugin3_set_sample_rate1(codec); + + //plugin3_setup_audio_output1(codec); + + //plugin3_buffers_on1(codec); + +} + + +static void cs_8409_enable_headset_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_enable_headset_streaming start\n"); + + // debug status check - 0x27 here + read_gpio_status_check(codec); + + // dont really have any idea how to get to here + // Im guessing from messaging + + //play_format_setup_headphone(codec); + cs43l83_headset_amp_format_setup(codec, 1, 1); + + cs42l83_headset_play_setup_on(codec); + + // power on audio output + cs42l83_set_power_state_on(codec, 0); + + cs42l83_headset_enable_on(codec); + + mycodec_info(codec, "cs_8409_enable_headset_streaming end\n"); + +} + +static void cs_8409_disable_headset_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_streaming start\n"); + + // why do we do the headphone disable/poweroff codec output twice?? + // but we do - repeatedly seen in logs + + // this was cs42l83_headset_disable(codec, 1); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming cs42l83_power_off_codec_output end\n"); + + cs42l83_headset_disable(codec); + + + //playstop_headset_amp_format_setup_disable_headphone(codec); + cs_8409_headset_amp_format_setup_disable(codec, 1); + + + // this was cs42l83_headset_disable(codec, 0); + + cs42l83_headset_enable_off(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming 1 cs42l83_power_off_codec_output start\n"); + + cs42l83_power_off_codec_output(codec); + + myprintk("snd_hda_intel: cs_8409_disable_headset_streaming 1 cs42l83_power_off_codec_output end\n"); + + + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_streaming end\n"); +} + + +static void cs_8409_headplay_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headplay_real start\n"); + + if (spec->headset_enable == 0) + { + mycodec_info(codec, "cs_8409_headplay_real enable 0\n"); + //cs_8409_headplay_real1(codec); + } + else if (spec->headset_enable == 1) + { + mycodec_info(codec, "cs_8409_headplay_real enable 1\n"); + //cs_8409_headplay_real1(codec); + cs_8409_enable_headset_streaming(codec); + //spec->headset_enable = 2; + } + else if (spec->headset_enable == 2) + { + mycodec_info(codec, "cs_8409_headplay_real enable 2\n"); + //cs_8409_headplay_real2(codec); + } + + mycodec_info(codec, "cs_8409_headplay_real end\n"); +} + + +static void cs_8409_headplaystop_real(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_headplaystop_real start\n"); + + cs_8409_disable_headset_streaming(codec); + + mycodec_info(codec, "cs_8409_headplaystop_real end\n"); +} + +static void cs_8409_enable_headset_mike_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_enable_headset_mike_streaming start\n"); + + // debug status check - 0x27 here + read_gpio_status_check(codec); + + // dont really have any idea how to get to here + // Im guessing from messaging + + + // NOTE - there are big ordering issues here + // - here we setup the headphone output before the mike + // - this maybe because Quicktime defaults to enabling play when recording + // unfortunately looks as tho linux tends to open the capture stream before the playback stream + // - so going to ignore this here + + // this sets up the headphones + // note this only does a partial headset amp setup compared to a base headset play + // power on audio output + //cs42l83_set_power_state_on(codec, 0); + //cs42l83_headset_enable_on(codec); + + //retval = cs42l83_headphone_sense(codec); + //mycodec_info(codec, "cs_8409_enable_headset_mike_streaming headphone sense 0x%x\n", retval); + //if (!(retval & 0x80)) + //{ + // dev_info(hda_codec_dev(codec), "cs_8409_enable_headset_mike_streaming headphone NOT PLUGGED IN UNIMPLEMENTED!!\n"); + //} + //retval = cs42l83_headphone_sense(codec); + //mycodec_info(codec, "cs_8409_enable_headset_mike_streaming headphone sense 0x%x\n", retval); + //if (!(retval & 0x80)) + //{ + // dev_info(hda_codec_dev(codec), "cs_8409_enable_headset_mike_streaming headphone NOT PLUGGED IN UNIMPLEMENTED!!\n"); + //} + + ////cs43l83_headset_amp_format_setup_partial + //cs43l83_headset_amp_format_setup(codec, 0); + + + cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + cs42l83_input_set_output_sample_rate(codec); + + cs42l83_mike_setup_audio_input(codec); + + cs42l83_mike_enable(codec); + + + // power on the codec/audio input + cs42l83_set_power_state_on(codec, 1); + + // unmute audio input + cs42l83_headset_mike_adc_unmutevol(codec, 1); + + + // for partial setup only + //cs42l83_headset_mike_pin_enable(codec); + //cs42l83_headset_mike_format_setup_enable(codec, 0, 1); + + + // this is all done in the capture hook after this call + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + mycodec_info(codec, "cs_8409_enable_headset_mike_streaming end\n"); +} + +static void cs_8409_disable_headset_mike_streaming(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_mike_streaming start\n"); + + + // NOTE - there are big ordering issues here + // although here the mike is turned off before the headphone output + + // mute ADC + cs42l83_headset_mike_adc_unmutevol(codec, 0); + + cs42l83_power_off_codec_input(codec); + + cs42l83_mike_disable(codec); + + cs42l83_headset_mike_format_setup_disable(codec); + + // and duplicate the above!! + cs42l83_headset_mike_adc_unmutevol(codec, 0); + cs42l83_power_off_codec_input(codec); + + + // the following is disabling the headphone component + // - assuming this is done by the playback hooks + + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + //cs42l83_buffers_onoff + //cs42l83_headset_power_off + //cs_8409_headset_amp_disable_TDM_proper (full) + //cs_8409_headset_amp_format_setup_disable + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + + // and re-enabling the headphone component????? + // igoring all the following for the moment + + // note there was a 7 second delay here - this is recextstop2/2c + + //cs43l83_headset_amp_format_setup (full) + //cs42l83_configure_int_mclk + //cs42l83_power_onoff + //cs42l83_configure_serial_port + //cs42l83_output_set_input_sample_rate + //cs42l83_setup_audio_output + //cs42l83_buffers_onoff + //cs42l83_set_power_state_on + //cs42l83_headset_enable_on + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + //cs_8409_external_device_unsolicited_response (continued) + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + + // then re-disabling!! + + // note there was a 5 second delay here - this is recextstop3 + + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + //cs42l83_headset_rcv_enable_off + //cs42l83_headset_power_off + //cs_8409_headset_amp_disable_TDM_proper - full + //cs42l83_headset_enable_off + //cs42l83_power_off_codec_output + + //cs_8409_external_device_unsolicited_response + //cs_8409_read_status_and_clear_interrupt + //read_gpio_status + //read_gpio_status + //cs42l83_read_status_and_clear_interrupt + //read_gpio_status + //cs42l83_disambiguate_ur_from_int + //read_gpio_status + + mycodec_info(codec, "snd_hda_intel: cs_8409_disable_headset_mike_streaming end\n"); +} + +static void cs_8409_headcapture_real(struct hda_codec *codec) +{ + //int retval; + struct cs8409_apple_spec *spec = codec->spec; + + mycodec_info(codec, "cs_8409_headcapture_real start\n"); + + if (spec->headset_enable == 0) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 0\n"); + //cs_8409_headcapture_real1(codec); + } + else if (spec->headset_enable == 1) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 1\n"); + //cs_8409_headcapture_real1(codec); + hda_check_power_state(codec, 0x1a, 11); + hda_check_power_state(codec, 0x3c, 11); + cs_8409_enable_headset_mike_streaming(codec); + hda_check_power_state(codec, 0x1a, 12); + hda_check_power_state(codec, 0x3c, 12); + //spec->headset_enable = 2; + } + else if (spec->headset_enable == 2) + { + mycodec_info(codec, "cs_8409_headcapture_real enable 2\n"); + //cs_8409_headcapture_real2(codec); + } + + mycodec_info(codec, "cs_8409_headcapture_real end\n"); +} + + +static void cs_8409_headcapturestop_real(struct hda_codec *codec) +{ + //int retval; + + mycodec_info(codec, "cs_8409_headcapturestop_real start\n"); + + cs_8409_disable_headset_mike_streaming(codec); + + mycodec_info(codec, "cs_8409_headcapturestop_real end\n"); +} + diff --git a/patch_cirrus_real84_i2c.h b/patch_cirrus_real84_i2c.h new file mode 100644 index 0000000..c31b1c1 --- /dev/null +++ b/patch_cirrus_real84_i2c.h @@ -0,0 +1,1955 @@ + +// this contains functions converted to use proper i2c calls + +// re-define this here so can turn this off for this file specifically +#define mycodec_i2c_local_info mycodec_i2c_info + +static void cs42l83_headset_button_detect_interrupts_off(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_button_detect_interrupts_off\n"); + + // this could be AppleHDAMikeyInternalCS8409::disableButtonDetection + // - this calls 0x1b7c and 0x1b7a + + //snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, 0x00000000); // 0x00170500 + hda_set_node_power_state(codec, codec->core.afg, AC_PWRST_D0); + + // register 0x1b7c - this is undocumented for 42l42 (reserved) - maybe more interrupt mask registers + // sort of assuming it shows which interrupts have occurred + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff (default) to 0xff (all masked) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + +} + +static void cs42l83_headset_set_hpout_clamp_disable(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_set_hpout_clamp_disable\n"); + + // AppleHDATDM_CS42L83::setHPOutClamp + + // register 0x1f06 - DAC Control 2 + // changed from 0x2 (default) to 0x06 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0602 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + +} + +static void cs42l83_complete_jack_detect(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_complete_jack_detect\n"); + + // in AppleHDAMikeyInternalCS8409::completeJackDetectUR + + // similar calls also appear in AppleHDATDM_CS42L83::enterStandby - but these are explicit calls in completeJackDetectUR + + // so this appears to change 0x1b75 from 0x9f to 0xb5 + // according to docs this is 0x80 LATCH_TO_VP set + // and HS_DETECT_LEVEL from 0x1f (default) to 0x35 + + // register 0x1b75 - Mic Detect Control 1 + // changed from 0x9f ((0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 1f) to 0xb5 (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x35) + + // maybe readMikey with 0x6 1st arg doesnt do anything?? + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x1 (Output HiZ, powered Down) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x759f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x75b5 i2c data 0x00b5 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7401 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x00b5, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0001, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_on\n"); + + // AppleHDAMikeyInternalCS8409::powerHSBIAS + + // readMikey analysis for AppleHDATDM_CS42L83::readMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 8 from it compare in range 0 to 5 + // (so 8 becomes 0, 9 becomes 1, 10 becomes 2, 11 becomes 3) + // 0x111f is loaded by default + // 8 0 is 0x111f + // 9 1 is 0x1120 + // 10 2 is 0x1121 + // 11 3 is 0x1124 + + // writeMikey analysis for AppleHDATDM_CS42L83::writeMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 6 from it compare in range 0 to 7 + // (so 8 becomes 2, 9 becomes 3, 10 becomes 4) + // 0x111f is loaded by default + // 8 2 is 0x111f + // 9 3 is 0x1120 + // 10 4 is 0x1121 + // 11 5 is 0x1124 + + // register 0x1b74 - Miscellaneous Detect Control + // value 0x1 (Output HiZ, powered Down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x0 (default) to 0x8 (manual disabled, hs bias closed, auto time 10 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set to 0x7 (Output 2.7V, powered Down) - headset interface functional + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8 to 0x0 (default) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7401 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2008 i2c data 0x0008 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7407 i2c data 0x0007 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2008 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x0000 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0008, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0007, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0000, 1); // snd_hda + +} + +static void cs42l83_enable_hs_auto_int_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_enable_hs_auto_int_on\n"); + + // AppleHDATDM_CS42L83::enableHSDetAutoInt(bool) + // this involves a read then write + + // register 0x131b - Codec Interrupt Mask + // changed from 0x3 (default) to 0x1 + // this is enabling auto headset detection (well HSDET_AUTO_DONE) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b01 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0001, 1); // snd_hda + +} + +static void cs42l83_unplug_interrupt_setup(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_unplug_interrupt_setup\n"); + + // in AppleHDAMikeyInternalCS8409::completeJackDetectUR + + // interrupt clearing seems to be triggered by reading registers in this routine (hence the name) + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Interrupt + // Detect Interrupt 1 Status + // value 0x00 + // register 0x1b79 - Detect Interrupt Mask 1 + // changed from 0xa0 to 0xc0 + // this masks TIP_SENSE_PLUG and unmasks TIP_SENSE_UNPLUG + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79a0 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x79c0 i2c data 0x00c0 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + newval = (retval & 0x9f) | 0x40; + + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, newval, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, 0x00c0, 1); // snd_hda + +} + +static void cs42l83_set_hpout_pulldown_off(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_off\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown + + // register 0x1f06 - DAC Control 2 + // changed from 0x6 to 0x86 (no HPOUT_PULL_DOWN) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0686 i2c data 0x0086 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0086, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x80, 1); // snd_hda + +} + +static void cs42l83_headset_detect_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_on\n"); + + // this is AppleHDAMikeyInternalCS8409::enableHSDetection + // this calls readMikey/writeMikey with either 0x8 arg or 0x9 arg + // I think 0x8 arg is one used in the log + + // readMikey analysis for AppleHDATDM_CS42L83::readMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 7 from it compare in range 0 to 5 + // (so 8 becomes 1, 9 becomes 2) + // 0x111f is loaded by default + // 1 is 0x1120 + + // writeMikey analysis for AppleHDATDM_CS42L83::writeMikey + // if 1st arg is equal or above 0x70 we send eg 0x1b70 (0x1b for our case) + // if 1st arg is below 0x70 we do special things + // subtract 5 from it compare in range 0 to 7 + // (so 8 becomes 3, 9 becomes 4) + // 0x111f is loaded by default + // 3 is 0x1120 + + // register 0x111f - Headset Detect Control 1 + // changed from 0x77 (default) to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0x00 (default) to 0xc2 + // 0xc0 headset detection automatic, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f77 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x20c2 i2c data 0x00c2 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x00c2, 1); // snd_hda + +} + +static void cs42l83_headset_detect_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_off\n"); + + // AppleHDAMikeyInternalCS8409::enableHSDetection + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0xc2 (default) to 0x82 + // 0x80 headset detection disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x20c2 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_headset_detect_onoff(struct hda_codec *codec, int onstate) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect_onoff\n"); + + // AppleHDAMikeyInternalCS8409::enableHSDetection + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0xc2 (default) to 0x82 + // 0x80 headset detection disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x20c2 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + if (onstate) + { + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + if (0xc0 >= retval) + { + newval = (retval & 0x3f) | 0x80; + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + } + } + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + + if (onstate) + newval = (retval & 0x3c) | 0x80; + else + newval = retval | 0xc0; + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda +} + +static void cs42l83_enable_hs_auto_int_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hs_auto_int_off\n"); + + // from AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // AppleHDATDM_CS42L83::enableHSDetAutoInt + + // register 0x131b - Codec Interrupt Mask + // changed from 0x3 (default) to 0x3 + // this is disabling auto headset detection (well HSDET_AUTO_DONE) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x1b00 i2c data 0x1b03 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x13 lo 0x1b03 i2c data 0x0003 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x131b, 1); // snd_hda + + newval = (retval & 0xfd) | 0x02; + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x131b, 0x0003, 1); // snd_hda + +} + +static int cs42l83_headset_type(struct hda_codec *codec) +{ + int retval_type; + + mycodec_i2c_local_info(codec, "cs42l83_headset_type\n"); + + // this is readMikey function 0xb + + // register 0x1124 - Headset Detect Status + // value 0x06 - 0x4 undocumented, headset type 0x02 (3) + + // types are 0x00 (1), 0x01 (2), 0x02 (3) and 0x3 (4) + // type 1 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Mic + // type 2 Pin 1 Left, Pin 2 Right, Pin 3 Mic, Pin 4 Gnd + // type 3 Pin 1 Left, Pin 2 Right, Pin 3 Gnd, Pin 4 Gnd + // type 4 Optical!! + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2400 i2c data 0x2406 + + retval_type = cs_8409_vendor_i2cRead(codec, 0x90, 0x1124, 1); // snd_hda + + return retval_type; +} + +static void cs42l83_set_hpout_pulldown_on(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_on\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown(bool) + + // register 0x1f06 - DAC Control 2 + // changed from 0x86 to 0x06 (HPOUT_PULL_DOWN on) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0686 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + +} + + +static void cs42l83_set_hpout_pulldown_onoff(struct hda_codec *codec, int onstate) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_pulldown_onoff\n"); + + // AppleHDATDM_CS42L83::setHPOutPulldown(bool) + + // register 0x1f06 - DAC Control 2 + // changed from 0x86 to 0x06 (HPOUT_PULL_DOWN on) + + // register 0x1f06 - DAC Control 2 + // changed from 0x6 to 0x86 (no HPOUT_PULL_DOWN) + + if (onstate) + { + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0686 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0606 i2c data 0x0006 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x00, 1); // snd_hda + } + else + { +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0686 i2c data 0x0086 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0086, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0xf0, 0x80, 1); // snd_hda + } + +} + + +static void cs42l83_set_hpout_clamp_enable(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_set_hpout_clamp_enable\n"); + + // AppleHDATDM_CS42L83::setHPOutClamp + + // register 0x1f06 - DAC Control 2 + // changed from 0x06 to 0x02 (default) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1f lo 0x0600 i2c data 0x0606 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0602 i2c data 0x0002 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1f06, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1f06, 0x04, 0x00, 1); // snd_hda +} + +static void cs42l83_headset_mike_detect_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_detect_off\n"); + + // in AppleHDAMikeyInternalCS8409::handleTypeDetectUR + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x7 to 0x7 (Output 2.7V, mic detect powered Down) - headset interface functional + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7407 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7407 i2c data 0x0007 + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + // this sets the detect mode to 0b00 (default, inactive) + newval = (retval & 0xe7); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0007, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_off\n"); + + // multiple calls to this + // AppleHDAMikeyInternalCS8409::handleTypeDetectUR + // AppleHDAMikeyInternalCS8409::completeJackDetectUR + // AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::powerHSBIAS(unsigned int) + + // register 0x1b74 - Miscellaneous Detect Control + // value 0x7 (Output 2.7V, mic level detect powered Down) - headset interface functional + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 (automatic disabled, auto time 50 microsecs) to 0x8a (automatic disabled, hs bias closed, auto time 50 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set 0x3 (default) (Output weak ground, mic level detect powered Down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8a to 0x82 ((automatic disabled, auto time 50 microsecs) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7407 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x208a i2c data 0x008a + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x208a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x008a, 1); // snd_hda + + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + + //IOSleep(0x28); + msleep(0x28); + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + + //IOSleep(0x14); + msleep(0x14); +} + + +static void cs42l83_enable_hsbias_auto_clamp_on(struct hda_codec *codec) +{ + + //int updval; + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_on\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 + + // explicit coding + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + // bit coding + //updval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //updval &= 0x3f; + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off(struct hda_codec *codec) +{ + + int updval; + int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + // set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x03 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + // explicit coding + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + + // bit coding + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + updval = retval & 0x3f; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + + updval = (retval & 0xb8) | 0x46; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + + +static void cs42l83_enable_hsbias_auto_clamp_off0(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off0\n"); + + // in AppleHDAMikeyInternalCS4208::handleTypeDetectUR + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x03 to 0x03 (HS Sense Bias trip 52 microamps + // set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x03 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7003 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_setup_button_detect(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_setup_button_detect\n"); + + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xff all masked + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xe7 (this appears to be a difference from the 42l42 doc + // - for the 42l42 0xe7 0x10 and 0x08 are reserved bits - maybe mike button bits) + // register 0x1b76 - Mic Detect Control 2 + // changed from 0x2f (default) to 0x3f (again undocumented bit) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x3 (0x02 hsbias ctl weak ground, 0x01 mic lvl detect powered down) + // register 0x1b78 - Detect Status 2 + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x1b (0x18 normal mode, 0x2 weak ground, 0x01 mic lvl detect powered down) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x1b to 0x9b (0x80 undocumented) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7ae7 i2c data 0x00e7 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7600 i2c data 0x762f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x763f i2c data 0x003f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x78d0 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741b i2c data 0x001b +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x741b +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x749b i2c data 0x009b + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00e7, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b76, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b76, 0x003f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x009b, 1); // snd_hda + +} + +static void cs42l83_power_hs_bias_button_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_hs_bias_button_on\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::powerHSBIAS + + // register 0x1b74 - Miscellaneous Detect Control + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + // value 0x9b (0x80 undocumented, 0x18 normal mode, 0x2 weak ground, 0x1 mic DC level detect powered down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 (automatic disabled, auto time 50 microsecs) to 0x8a (automatic disabled, hs bias closed, auto time 50 microsecs) + // register 0x1b74 - Miscellaneous Detect Control + // set 0x9f (0x80 undocumented, 0x18 normal mode, 0x6 2.7V headset interface functional, 0x1 mic DC level detect powered down) + // register 0x1120 - Headset Detect Control 2 + // changed from 0x8a to 0x82 ((automatic disabled, auto time 50 microsecs) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x749b +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x208a i2c data 0x008a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x749f i2c data 0x009f +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x208a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x009f, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off1(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off1\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 -> 0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static int cs42l83_handle_button_detect(struct hda_codec *codec) +{ + int ret_ignore; + int ret_mask; + int ret_sense; + int ret_state1 = 0; + int ret_state2 = 0; + int ret_detect1 = 0; + int ret_detect2 = 0; + + mycodec_i2c_local_info(codec, "cs42l83_handle_button_detect\n"); + + // AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xe7 (0x18 unmasked - undocumented - maybe mike buttons - rest masked) + // register 0x1b7c - this is undocumented for 42l42 (reserved) + // Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xe7 state of 0x1b7a) + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xe7 to 0xff + // register 0x1b77 - Detect Status 1 + // value 0x92 0x80 HP plugged bias 0x12 + // register 0x1b78 - Detect Status 2 + // value 0x40 (undocumented - maybe this indicates we have buttons) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x9f to 0x1f + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7ae7 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7ae7 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7700 i2c data 0x7792 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7840 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7840 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x749f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741f i2c data 0x001f + + ret_mask = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + //if ((ret_mask & 0xe7) != 0xe7) + if ((~ret_mask & 0x18) != 0x18) + { + printk("snd_hda_intel: cs42l83_handle_button_detect invalid mask 0x%08x\n",ret_mask); + return 0; + } + + // dont get this - return is 0 - we have no interrupts signalled in the unmasked section?? + // the cs42l42 documentation mentions shadow registers which says reading this register + // loads the shadow register contents to register 0x1b7c - but dont see such a 2nd read here?? + // maybe we are just clearing any bad initial state?? + ret_ignore = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + // this is headphone sense - not clear what we do if dont sense headphones + ret_sense = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b77, 1); // snd_hda + + ret_state1 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + ret_state2 = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001f, 1); // snd_hda + + // possible coding from assembler - do not rely on!! + //ret_detect1 = (ret_state >> 2) & 0x7; + //ret_detect2 = (ret_state + ret_state) & 0x6; + //ret_state1 = (((ret_state1 >> 0x7) | ret_detect2) >> 4) & 0x7; + + // this seems to be returning 0x40 - which is undocumented + // for the moment going to assume 0x40 means we have buttons + return ((ret_sense & 0xff) << 16) | ((ret_state2 & 0xff) << 8) | (ret_state1 & 0xff); +} + +static int cs42l83_mike_connected(struct hda_codec *codec) +{ + int ret_connect = 0; + int retval = 0; + + mycodec_i2c_local_info(codec, "cs42l83_mike_connected\n"); + + // from AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // from AppleHDAMikeyInternal::configureHeadset(unsigned char, unsigned char, unsigned char) + + // AppleHDAMikeyInternalCS8409::microphoneConnected(bool*) + + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x1f to 0x1e + // register 0x1b75 - Mic Detect Control 1 + // changed from 0xb5 to 0xb5 (0x80 LATCH_TO_VP set, HS_DETECT_LEVEL 0x35) + // register 0x1b78 - Detect Status 2 + // value 0x42 (0x40 unknown 0x2 hsbias_in true) + // register 0x1b74 - Miscellaneous Detect Control + // set to 0x1f + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x741f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741e i2c data 0x001e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7500 i2c data 0x75b5 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x75b5 i2c data 0x00b5 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7800 i2c data 0x7842 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x741f i2c data 0x001f + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b75, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b75, 0x00b5, 1); // snd_hda + + // this is returning a different value - now get 0x42 + // assuming 0x40 indicates we have buttons + // and 0x02 seems to be indication we have a mike (from code analysis) + ret_connect = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b78, 1); // snd_hda + + // this converts 0x2 to a bool response ie 0x0 or 0x1 + retval = ((ret_connect > 1) & 0x01); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x001f, 1); // snd_hda + + return ret_connect; +} + + +static void cs42l83_configure_int_mclk(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_configure_int_mclk\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_configureIntMClk + + // configuring clocks in the CS42L83 is extremely complex + // multiple divisors/multipliers + // only some registers decoded here + + // register 0x1007 - Serial Port SRC Control + // changed from 0x10 to 0x10 - bypass equalizer + + // register 0x1009 - MCLK Control + // .... + + // register 0x1208 - ASP Frame Configuration + // set to 0x00 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0700 i2c data 0x0710 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0710 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0902 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0c02 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x02ff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x039c i2c data 0x009c +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0470 i2c data 0x0070 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x052e i2c data 0x002e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x1b03 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0a5d i2c data 0x005d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0810 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x05ff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0600 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0100 i2c data 0x0100 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0800 i2c data 0x0000 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1007, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1007, 0x0010, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1009, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120c, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1502, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1503, 0x009c, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1504, 0x0070, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1505, 0x002e, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x151b, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x150a, 0x005d, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1508, 0x0010, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1205, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1206, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1201, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1201, 0x0000, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0000, 1); // snd_hda + +} + +static void cs42l83_headset_power_on_on_nouse(struct hda_codec *codec) +{ + // this function replaced by cs42l83_power_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_power_on_on_nouse\n"); + + // AppleHDATDM_CS42L83::powerOn + + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0700 i2c data 0x0720 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0701 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1207, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1207, 0x0020, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0001, 1); // snd_hda + +} + +static void cs42l83_power_onoff(struct hda_codec *codec, bool onflag) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_onoff\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::powerOn + + // bool arg switches order of 0x1107/0x1501 - on (0x1207) 0x1501/0x1107 - off 0x1107/0x1501 + + if (onflag) + { + // register 0x1207 - ASP Clock Configuration 1 + // changed from 0x00 to 0x20 - ASP_LCPOL_OUT inverted + // register 0x1501 - PLL Control 1 + // set to 0x01 - powered on + // register 0x1107 - Oscillator Switch Control + // set to 0x01 - SCLK is present and the internal MCLK is sourced from the SCLK pin + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0700 i2c data 0x0700 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0101 i2c data 0x0001 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0701 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1207, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1207, 0x0020, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0001, 1); // snd_hda + } + else + { + // register 0x1107 - Oscillator Switch Control + // set to 0x00 (power down, in transition) + // register 0x1501 - PLL Control 1 + // set to 0x00 (powered off) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0000, 1); // snd_hda + } +} + +static void cs42l83_configure_serial_port(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_configure_serial_port\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_configureSerialPort + + // register 0x2505 - Serial Port Transmit Isochronous Control + // changed from 0x04 to 0x05 (NFS mode, 48k isochronous stream) + // register 0x2502 - Serial Port Receive Isochronous Control + // changed from 0x04 to 0x05 (NFS mode, 48k isochronous stream) + // register 0x2503 - Serial Port Receive Sample Rate + // changed from 0x8c to 0x8a (48 kHz to 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xcc to 0xca (48 kHz to 44.1 kHz) + // register 0x1208 - ASP Frame Configuration + // changed from 0x00 to 0x02 (ASP_STP from high to low, ASP frame start 1.0 delay) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0500 i2c data 0x0504 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x0505 i2c data 0x0005 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0200 i2c data 0x0204 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x0205 i2c data 0x0005 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0300 i2c data 0x038c +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x038a i2c data 0x008a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06cc +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0800 i2c data 0x0800 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0802 i2c data 0x0002 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2505, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2505, 0x0005, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2502, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2502, 0x0005, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2503, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2503, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1208, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0002, 1); // snd_hda + +} + +static void cs42l83_output_set_input_sample_rate(struct hda_codec *codec) +{ + // changed name from cs42l83_set_sample_rate + // NOTA BENE - it says output sample rate because its the sample rate for digital data + // coming into the cs42l83 and being converted to analog output + // - so the register is labelled the Input sample rate!! + int ret_fs_rate; + + mycodec_i2c_local_info(codec, "cs42l83_output_set_input_sample_rate\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // register 0x2601 - SRC Input Sample Rate + // changed from 0x40 to 0x4a (0x0a is 44.1 kHz) + // register 0x2503 - Serial Port Receive Sample Rate + // changed from 0x8a to 0x8a (0x0a is 44.1 kHz) + // register 0x120a - Input ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x01 (0x01 Enable IASRC 96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0100 i2c data 0x0140 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x014a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0300 i2c data 0x038a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x038a i2c data 0x008a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0a00 i2c data 0x0a00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0a00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0900 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0901 i2c data 0x0001 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2601, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2601, 0x004a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2503, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2503, 0x008a, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x120a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120a, 0x0000, 1); // snd_hda + + // correctly implement this to mask in the right bit as per docs + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0001, 1); // snd_hda + ret_fs_rate = cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + ret_fs_rate |= 0x1; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, ret_fs_rate, 1); // snd_hda + +} + +static void cs42l83_setup_audio_output(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_setup_audio_output\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioOutput + + // register 0x1f06 - DAC Control 2 + // set to 0x2 (default) DAC HPF enable + // register 0x2001 - HP Control + // changed from 0x0d (default) to 0x0d + // register 0x2a04 - ASP Receive DAI0 Channel 1 Bit Start LSB + // set to 0x00 + // register 0x2a07 - ASP Receive DAI0 Channel 2 Bit Start LSB + // set to 0x20 (is this 32 bits?? - maybe) + // register 0x2301 - Mixer Channel A Input Volume + // set to 0x00 (0 dB - positive values negative dB as usual 0x3f mute (default)) + // register 0x2303 - Mixer Channel B Input Volume + // set to 0x00 (0 dB - positive values negative dB as usual 0x3f mute (default)) + // register 0x2a02 - ASP Receive DAI0 Channel 1 Phase and Resolution + // set to 0x02 (24 bits per sample) + // (0x00 8 bits, 0x01 16 bits, 0x03 32 bits (default)) + // register 0x2a05 - ASP Receive DAI0 Channel 2 Phase and Resolution + // set to 0x02 (24 bits per sample) + // (0x00 8 bits, 0x01 16 bits, 0x03 32 bits (default)) + // register 0x100b - Slow Start Enable + // changed from 0x70 (default) to 0x00 (disabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1f lo 0x0602 i2c data 0x0002 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x010d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x010d i2c data 0x000d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0400 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0720 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x23 lo 0x0300 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0202 i2c data 0x0002 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0502 i2c data 0x0002 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x10 lo 0x0b00 i2c data 0x0b70 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x0b00 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1f06, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x000d, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a04, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a07, 0x0020, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2301, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2303, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a02, 0x0002, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a05, 0x0002, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x100b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x100b, 0x0000, 1); // snd_hda + +} + + +static void cs42l83_headset_rcv_enable_on(struct hda_codec *codec) +{ + int retval; + + // this function has been replaced by cs42l83_buffers_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_rcv_enable_on\n"); + + // AppleHDATDM_CS42L83::enable + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x010c i2c data 0x000c + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x000c, 1); // snd_hda + +} + +static void cs42l83_buffers_onoff(struct hda_codec *codec, bool onflag) +{ + + mycodec_i2c_local_info(codec, "cs42l83_buffers_onoff\n"); + + // in AppleHDATDM_CS42L83::enable + + // yet again this has a bool arg which is enable/disable + + if (onflag) + { + // register 0x2a01 - ASP Receive Enable + // set to 0x0c (populate channel buffers for 1 and 2) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x010c i2c data 0x000c + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x000c, 1); // snd_hda + } + else + { + // register 0x2a01 - ASP Receive Enable + // set to 0x00 (channel buffers not populated) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x0000, 1); // snd_hda + } +} + +static void cs42l83_set_power_state_on_nouse(struct hda_codec *codec, int dummy) +{ + + mycodec_i2c_local_info(codec, "cs42l83_set_power_state_on_nouse\n"); + + // AppleHDATDM_CS42L83::setPowerState + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x01fe +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b60 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x13 lo 0x0b00 i2c data 0x0b24 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x130b, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + +} + + +static void cs42l83_headset_enable_on(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_enable_on\n"); + + // looks as tho this is only enabling the headphone component + // - doesnt seem to be needed for the headset mike to work + // (unless it just happens the headphone component is always enabled so + // dont see it for the headset mike) + + // AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::enableHeadphones + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e99 i2c data 0x0099 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x0830 i2c data 0x0030 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x21 lo 0x0200 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x010d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0099, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b08, 0x0030, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2102, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0000, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x0001, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x2001, 0x0c, 0x00, 1); // snd_hda + +} + + +static void cs42l83_plugin_interrupt_setup(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_plugin_interrupt_setup\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // this enables the headphone plugin sense interrupt + + // register 0x1b7b - this is undocumented for 42l42 but labelled in fig 4-45 as Interrupt + // now think this shows which interrupt of 0x1b79 was triggered + // reading this clears any interrupt which will send a UR + // value 0x00 + // register 0x1b79 - Detect Interrupt Mask 1 + // changed from 0xe0 (default) to 0xa0 - unmasks TIP_SENSE_PLUG + // changed from 0xc0 (TIP_SENSE_UNPLUG unmasked) to 0xa0 (TIP_SENSE_UNPLUG masked TIP_SENSE_PLUG unmasked) + + // so reading 0x1b7b seems to lead to 2 UNSOL events + // one with interrupt set followed by one with interrupt cleared + + mycodec_info(codec, "command cs42l83_plugin_interrupt_setup start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7b00 i2c data 0x7b00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7900 i2c data 0x79c0 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x79a0 i2c data 0x00a0 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7b, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b79, 1); // snd_hda + + newval = (retval & 0x9f) | 0x20; + + myprintk_dbg("snd_hda_intel: cs42l83_plugin_interrupt_setup 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b79,newval,0x00a0,retval,0x00c0); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b79, 0x00a0, 1); // snd_hda + + mycodec_info(codec, "command cs42l83_plugin_interrupt_setup end\n"); +} + +static void cs42l83_headset_detect2_off(struct hda_codec *codec) +{ + int retval; + int newval; + + mycodec_i2c_local_info(codec, "cs42l83_headset_detect2_off\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // register 0x1b7c - Detect Interrupt 2 Status + // value 0x00 (none ie inverse of 0xff state of 0x1b7a) + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff to 0xff (all masked) + // register 0x1b74 - Miscellaneous Detect Control + // changed from 0x3 (default) to 0x3 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c00 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7400 i2c data 0x7403 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7403 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + + newval = (retval | 0x38); + + myprintk_dbg("snd_hda_intel: cs42l83_headset_detect2_off 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b7a,newval,0x00ff,retval,0x00ff); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + + + retval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b74, 1); // snd_hda + + newval = (retval & 0xe7); + + myprintk_dbg("snd_hda_intel: cs42l83_headset_detect2_off 0x%04x 0x%04x: 0x%04x (0x%04x 0x%04x 0x%04x)",0x90,0x1b7c,newval,0x00a0,retval,0x00c0); + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b74, 0x0003, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off3(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off3\n"); + + // in AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0xc6 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x70c6 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_disable_button_interrupts(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_disable_button_interrupts\n"); + + // AppleHDAMikeyInternalCS8409::setupButtonDetection + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x04 + // register 0x1b7a - Detect Interrupt Mask 2 + // value 0xfc + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xff + // register 0x1b7a - Detect Interrupt Mask 2 + // set to 0xff + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c04 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7afc +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7aff i2c data 0x00ff + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00ff, 1); // snd_hda + +} + +static void cs42l83_unplug_headset_detect_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_unplug_headset_detect_off\n"); + + // this is AppleHDAMikeyInternalCS8409::enableHSDetection + // this calls readMikey/writeMikey with 0x8 arg and 0x9 arg + + // register 0x111f - Headset Detect Control 1 + // changed from 0x72 to 0x72 + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 to 0x82 + // 0x80 headset detection automatic, disabled, 0x2 HSDET_AUTO_TIME 50 microsecs + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x1f00 i2c data 0x1f72 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x1f72 i2c data 0x0072 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2082 i2c data 0x0082 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x111f, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x111f, 0x0072, 1); // snd_hda + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0082, 1); // snd_hda + +} + +static void cs42l83_headset_switch_control(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_switch_control\n"); + + // from AppleHDAMikeyInternalCS8409::handleJackDisconnectUR + + // this is likely writeMikey 0x9, 0xa making it plausibly AppleHDAMikeyInternalCS8409::configureForUSHeadset + + // register 0x1120 - Headset Detect Control 2 + // changed from 0x82 to 0x42 + // 0x40 headset detection manual, 0x2 HSDET_AUTO_TIME 50 microsecs + + // I think this is writeMikey with 0xa arg + + // register 0x1121 - Headset Switch Control + // set to 0xa6 + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x2000 i2c data 0x2082 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x2042 i2c data 0x0042 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x21a6 i2c data 0x00a6 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1120, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1120, 0x0042, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1121, 0x00a6, 1); // snd_hda + +} + + +static void cs42l83_headset_enable_off(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_enable_off\n"); + + // in AppleHDATDM_CS42L83::enable - 0x22cb + // from AppleHDATDM_CS42L83::_powerOffCodecOutput + // AppleHDATDM_CS42L83::enableHeadphones(bool) + + // register 0x2001 - HP Control + // changed from 0x01 to 0x0d + // 0x107e - undocumented Global + // set to 0x99 + // 0x1b08 - undocumented Headset Interface + // set to 0x10 + // 0x2102 - Class H?? DPn Port Control?? + // set to 0x20 + // 0x107e - undocumented Global + // set to 0x00 + + myprintk("snd_hda_intel: cs42l83_headset_enable_off start\n"); + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x20 lo 0x0100 i2c data 0x0101 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x20 lo 0x010d i2c data 0x000d +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e99 i2c data 0x0099 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x0810 i2c data 0x0010 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x21 lo 0x0220 i2c data 0x0020 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x10 lo 0x7e00 i2c data 0x0000 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x2001, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x2001, 0x000d, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x2001, 0x0c, 0x0c, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0099, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b08, 0x0010, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2102, 0x0020, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x107e, 0x0000, 1); // snd_hda + + myprintk("snd_hda_intel: cs42l83_headset_enable_off end\n"); + +} + + +static void cs42l83_power_off_codec_output(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_off_codec_output\n"); + + // in AppleHDATDM_CS42L83::enable or AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::_powerOffCodecOutput + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0x96 to 0x9e (0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered to + // 0x40 ASP Input, 0x20 Mixer, 0x1 codec powered) + // register 0x1101 - Power Down Control 1 + // changed from 0x9e (0x40 ASP Input, 0x20 Mixer, 0x1 codec powered) to 0xfe (default - 0x1 codec powered)) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0196 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x019e i2c data 0x009e +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x019e +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x01fe i2c data 0x00fe + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x009e, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x08, 0x08, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x00fe, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x60, 0x60, 1); // snd_hda +} + +static void cs42l83_power_off_codec_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_power_off_codec_input\n"); + + // in AppleHDATDM_CS42L83::enable or AppleHDATDM_CS42L83::setPowerState + // AppleHDATDM_CS42L83::_powerOffCodecInput + + // note there is a call to AppleHDATDM_CS42L83::enableExtMicrophone(0) in this routine + // ah - thats the 0x1d03 mute/unmute volume register + + // register 0x1101 - Power Down Control 1 + // 0xff powered down 0x00 powered up + // changed from 0x12 to 0x16 (0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x4 ADC, 0x1 codec powered to + // 0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPout, 0x1 codec powered) + // register 0x1101 - Power Down Control 1 + // changed from 0x16 (0x80 ASP Output, 0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered) to + // 0x96 (0x40 ASP Input, 0x20 Mixer, 0x08 HPOut, 0x1 codec powered) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0112 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0116 i2c data 0x0016 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x11 lo 0x0100 i2c data 0x0116 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0196 i2c data 0x0096 + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0016, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x04, 0x04, 1); // snd_hda + + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1101, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1101, 0x0096, 1); // snd_hda + + cs_8409_vendor_i2cWriteMask(codec, 0x90, 0x1101, 0x80, 0x80, 1); // snd_hda +} + +static void cs42l83_headset_rcv_enable_off(struct hda_codec *codec) +{ + // this function has been replaced by cs42l83_buffers_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_rcv_enable_off\n"); + + // in AppleHDATDM_CS42L83::enable + // this is only place we have a 0x2a01 + + // yet again this has a bool arg which is enable/disable + + // register 0x2a01 - ASP Receive Enable + // set to 0x00 (channel buffers not populated) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x2a lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2a01, 0x0000, 1); // snd_hda + +} + +static void cs42l83_headset_power_off(struct hda_codec *codec) +{ + // this function replaced by cs42l83_power_onoff + + mycodec_i2c_local_info(codec, "cs42l83_headset_power_off\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::powerOn + // likely power off - bool arg switches order of 0x1107/0x1501 - on (0x2a10) 0x1501/0x1107 - off 0x1107/0x1501 + + // register 0x1107 - Oscillator Switch Control + // set to 0x00 (power down, in transition) + // register 0x1501 - PLL Control 1 + // set to 0x00 (powered off) + + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x11 lo 0x0700 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x15 lo 0x0100 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0000, 1); // snd_hda + +} + +//static void cs42l83_line_or_mike_set_sample_rate(struct hda_codec *codec) +static void cs42l83_input_set_output_sample_rate(struct hda_codec *codec) +{ + // changed name from cs42l83_line_or_mike_set_sample_rate + // NOTA BENE - it says input sample rate because its the sample rate for data + // from analog inputs converted to digital and going out of the cs42l83 + // - so the register is labelled the Output sample rate!! + + int ret_fs_rate; + + mycodec_i2c_local_info(codec, "cs42l83_input_set_output_sample_rate\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // is this for line or mike?? + + // register 0x2609 - SRC Output Sample Rate + // changed from 0x40 to 0x4a (0x0a is 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xca to 0xca (0x0a is 44.1 kHz) + // register 0x120b - Output ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x03 (0x01 Enable IASRC 96K and lower rates, 0x02 Enable OASRC96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0900 i2c data 0x0940 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x094a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06ca +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0b00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0901 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0903 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2609, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2609, 0x004a, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x120b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120b, 0x0000, 1); // snd_hda + + // correctly implement this to mask in the right bit as per docs + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0003, 1); // snd_hda + ret_fs_rate = cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + ret_fs_rate |= 0x2; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, ret_fs_rate, 1); // snd_hda + +} + +static void cs42l83_mike_setup_audio_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_setup_audio_input\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioInput + + // register 0x2903 - ASP Transmit Channel Phase and Resolution + // register 0x2905 - ASP Transmit Channel 1 Bit Start LSB + // register 0x290b - ASP Transmit Channel 2 Bit Start LSB + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x29 lo 0x0300 i2c data 0x030f +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x030a i2c data 0x000a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0500 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0b20 i2c data 0x0020 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2903, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2903, 0x000a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2905, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x290b, 0x0020, 1); // snd_hda + +} + +static void cs42l83_mike_enable(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_enable\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x03 (ASP Transmit Channel 1 enable (0x01), ASP Transmit Channel 2 enable (0x02)) + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x01 (Enabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0203 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0001, 1); // snd_hda + +} + + +static void cs42l83_mike_disable(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_mike_disable\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x00 (Disabled) + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x00 (disabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0100 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0200 i2c data 0x0000 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0000, 1); // snd_hda + +} + +//static void cs42l83_line_or_mike_set_sample_rate1(struct hda_codec *codec) +static void cs42l83_input_set_output_sample_rate1(struct hda_codec *codec) +{ + // changed name from cs42l83_line_or_mike_set_sample_rate1 + // NOTA BENE - it says input sample rate because its the sample rate for data + // from analog inputs converted to digital and going out of the cs42l83 + // - so the register is labelled the Output sample rate!! + // NOTA BENE - NOT fixed to do bit op for 0x1209 register!!! + + mycodec_i2c_local_info(codec, "cs42l83_input_set_output_sample_rate1\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setSampleRate + + // is this for line or mike?? + + // register 0x2609 - SRC Output Sample Rate + // changed from 0x4a to 0x4a (0x0a is 44.1 kHz) + // register 0x2506 - Serial Port Transmit Sample Rate + // changed from 0xca to 0xca (0x0a is 44.1 kHz) + // register 0x120b - Output ASRC Clock Select + // changed from 0x00 to 0x00 (0x00 6 MHz) + // register 0x1209 - FS Rate Enable + // changed from 0x00 to 0x01 (0x01 Enable IASRC 96K and lower rates) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x26 lo 0x0900 i2c data 0x094a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x26 lo 0x094a i2c data 0x004a +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x25 lo 0x0600 i2c data 0x06ca +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x25 lo 0x06ca i2c data 0x00ca +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0b00 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0b00 i2c data 0x0000 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x12 lo 0x0900 i2c data 0x0903 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x12 lo 0x0903 i2c data 0x0003 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2609, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2609, 0x004a, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x2506, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2506, 0x00ca, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x120b, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x120b, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1209, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1209, 0x0003, 1); // snd_hda + +} + +static void cs42l83_line_or_mike_setup_audio_input(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_line_or_mike_setup_audio_input\n"); + + // in AppleHDATDM_CS42L83::enable + + // AppleHDATDM_CS42L83::_setupAudioInput + + // register 0x2903 - ASP Transmit Channel Phase and Resolution + // register 0x2905 - ASP Transmit Channel 1 Bit Start LSB + // register 0x290b - ASP Transmit Channel 2 Bit Start LSB + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x29 lo 0x0300 i2c data 0x030a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x030a i2c data 0x000a +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0500 i2c data 0x0000 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0b20 i2c data 0x0020 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x2903, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2903, 0x000a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2905, 0x0000, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x290b, 0x0020, 1); // snd_hda + +} + +static void cs42l83_line_or_mike_enable_nouse(struct hda_codec *codec) +{ + + mycodec_i2c_local_info(codec, "cs42l83_line_or_mike_enable_nouse\n"); + + // in AppleHDATDM_CS42L83::enable + + // register 0x2902 - ASP Transmit Channel Enable + // set to 0x03 (ASP Transmit Channel 1 enable (0x01), ASP Transmit Channel 2 enable (0x02)) + // register 0x2901 - ASP Transmit Size and Enable + // set to 0x01 (Enabled) + +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0203 i2c data 0x0003 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x29 lo 0x0101 i2c data 0x0001 + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2902, 0x0003, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x2901, 0x0001, 1); // snd_hda + +} + +static void cs42l83_headset_mike_pin_enable(struct hda_codec *codec) +{ + int retval = 0; + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_pin_enable\n"); + + // why twice?? + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 14146); // 0x03cf0700 + + retval = snd_hda_codec_read_check(codec, 0x3c, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00000000, 0x00000020, 14147); // 0x03cf0700 + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + + snd_hda_codec_write(codec, 0x3c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00000020); // 0x03c70720 +// snd_hda: 60 ['AC_PINCTL_IN_EN'] + +} + +static void cs42l83_configure_headset_button_interrupts(struct hda_codec *codec) +{ + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_configure_headset_button_interrupts\n"); + + // from AppleHDAMikeyInternalCS8409::handleButtonDetectUR(unsigned int) + + // AppleHDAMikeyInternalCS8409::configureHeadset + // - this calls 0x1b7c and 0x1b7a + + // register 0x1b7c - Detect Interrupt Status 1 + // value 0x00 + // register 0x1b7a - Detect Interrupt Mask 2 + // changed from 0xff to 0xdc (0x23 unmasked, 0x18 masked - undocumented - maybe mike buttons - rest unmasked) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7c00 i2c data 0x7c40 +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7a00 i2c data 0x7aff +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7adc i2c data 0x00dc + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7c, 1); // snd_hda + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b7a, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b7a, 0x00dc, 1); // snd_hda + +} + +static void cs42l83_enable_hsbias_auto_clamp_off2(struct hda_codec *codec) +{ + + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_enable_hsbias_auto_clamp_off2\n"); + + // in AppleHDAMikeyInternalCS8409::setupButtonDetection + + // AppleHDAMikeyInternalCS8409::enableHSBIASautoclamp + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0x06 + // then set to 0x46 (Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7006 i2c data 0x0006 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x7046 i2c data 0x0046 + + cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0006, 1); // snd_hda + + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x0046, 1); // snd_hda + +} + +static void cs42l83_hsbias_sense_on(struct hda_codec *codec) +{ + + int updval; + //int retval; + + mycodec_i2c_local_info(codec, "cs42l83_hsbias_sense_on\n"); + + // in AppleHDAMikeyInternalCS8409::handleButtonDetectUR + + // register 0x1b70 - HSBIAS Sense and Hi-Z Autocontrol + // changed from 0x46 to 0xc6 + // set to 0xc6 (HS Bias Sense Enable (0x80) Tip Sense Enable (0x40) HS Sense Bias trip 93 microamps (0x06 ->0x06) ) + +// snd_hda i2cPagedRead i2c address 0x90 i2c reg hi 0x1b lo 0x7000 i2c data 0x7046 +// snd_hda i2cPagedWrite i2c address 0x90 i2c reg hi 0x1b lo 0x70c6 i2c data 0x00c6 + + // explicit coding + //cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + //cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, 0x00c6, 1); // snd_hda + + // bit coding + updval = cs_8409_vendor_i2cRead(codec, 0x90, 0x1b70, 1); // snd_hda + updval |= 0x80; + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1b70, updval, 1); // snd_hda + +} + + +static void cs42l83_headset_mike_adc_unmutevol(struct hda_codec *codec, int unmute) +{ + + mycodec_i2c_local_info(codec, "cs42l83_headset_mike_adc_unmutevol\n"); + + // in AppleHDATDM_CS42L83::enableExtMicrophone + + if (unmute) + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1d03, 0x0000, 1); // snd_hda + else + cs_8409_vendor_i2cWrite(codec, 0x90, 0x1d03, 0x0080, 1); // snd_hda +} + diff --git a/patch_cs8409.c b/patch_cs8409.c index e500067..6bbad52 100644 --- a/patch_cs8409.c +++ b/patch_cs8409.c @@ -1444,6 +1444,8 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac } } +static int patch_cs8409_apple(struct hda_codec *codec); + static int patch_cs8409(struct hda_codec *codec) { int err; @@ -1451,8 +1453,19 @@ static int patch_cs8409(struct hda_codec *codec) if (!cs8409_alloc_spec(codec)) return -ENOMEM; + printk("snd_hda_intel: Primary patch_cs8409\n"); + snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups); + // this seems the easiest way to separate and jump into the code for handling Apple machines using the 8409 + // note now freeing the just allocated spec - this undos the delayed work as not using mutex yet + if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET) { + printk("snd_hda_intel: Primary patch_cs8409 NOT FOUND trying APPLE\n"); + cs8409_free(codec); + err = patch_cs8409_apple(codec); + return err; + } + codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id, codec->bus->pci->subsystem_vendor, codec->bus->pci->subsystem_device); @@ -1469,6 +1482,12 @@ static int patch_cs8409(struct hda_codec *codec) return 0; } + +// for the moment split the new code into an include file + +#include "patch_cirrus_apple.h" + + static const struct hda_device_id snd_hda_id_cs8409[] = { HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), {} /* terminator */ diff --git a/patch_cs8409.h b/patch_cs8409.h index e4bd2e1..1308f7a 100644 --- a/patch_cs8409.h +++ b/patch_cs8409.h @@ -299,6 +299,23 @@ struct cs8409_cir_param { unsigned int coeff; }; + +#ifdef APPLE_CODECS +struct unsol_item { + struct list_head list; + unsigned int idx; + unsigned int res; +}; +struct hda_cvt_setup_apple { + hda_nid_t nid; + u8 stream_tag; + u8 channel_id; + u16 format_id; + unsigned char active; /* cvt is currently used */ + unsigned char dirty; /* setups should be cleared */ +}; +#endif + struct sub_codec { struct hda_codec *codec; unsigned int addr; @@ -309,6 +326,9 @@ struct sub_codec { unsigned int hp_jack_in:1; unsigned int mic_jack_in:1; +#ifdef APPLE_CODECS + unsigned int linein_jack_in:1; +#endif unsigned int suspended:1; unsigned int paged:1; unsigned int last_page; @@ -342,6 +362,126 @@ struct cs8409_spec { unsigned int init_done:1; unsigned int build_ctrl_done:1; +#ifdef APPLE_CODECS + + // additional data for Apple 8409 system + + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* digital beep */ + hda_nid_t beep_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + + // so it appears we have "concurrency" in the linux HDA code + // in that if unsolicited responses occur which perform extensive verbs + // the hda verbs are intermixed with eg extensive start playback verbs + // on OSX we appear to have blocks of verbs during which unsolicited responses + // are logged but the unsolicited verbs occur after the verb block + // this flag is used to flag such verb blocks and the list will store the + // responses + // we use a pre-allocated list - if we have more than 10 outstanding unsols + // we will drop + // not clear if mutexes would be the way to go + int block_unsol; + struct list_head unsol_list; + struct unsol_item unsol_items_prealloc[10]; + int unsol_items_prealloc_used[10]; + + // add in specific nids for the intmike and linein as they seem to swap + // between macbook pros (14,3) and imacs (18,3) + int intmike_nid; + int linein_nid; + int intmike_adc_nid; + int linein_amp_nid; + + // the following flag bits also need swapping + int reg9_intmike_dmic_mo; + int reg9_linein_dmic_mo; + int reg82_intmike_dmic_scl; + int reg82_linein_dmic_scl; + + + // add explicit stream format store entries as per hda_codec using a local definition + // of hda_cvt_setup (which is local to hda_codec.c) + // also use explicit nid versions + // (except that means either need explicit functions for each nid or have to lookup + // nid each time want to use in a generic function with nid argument) + struct hda_cvt_setup_apple nid_0x02; + struct hda_cvt_setup_apple nid_0x03; + struct hda_cvt_setup_apple nid_0x0a; + struct hda_cvt_setup_apple nid_0x22; + struct hda_cvt_setup_apple nid_0x23; + struct hda_cvt_setup_apple nid_0x1a; + + + // new item to deal with jack presence as Apple (and now Dell) seems to have barfed + // the HDA spec by using a separate headphone chip + int jack_present; + + // save the type of headphone connected + int headset_type; + + // if headphone has mike or not + int have_mike; + + // if headphone has buttons or not + int have_buttons; + + // current stream channel count + int stream_channels; + + // set when playing for plug/unplug events while playing + int playing; + + // set when capturing for plug/unplug events while capturing + int capturing; + + // changing coding - OSX sets up the format on plugin + // then does some minimal setup when start play + // initial coding delayed any format setup till actually play + // this works for no mike but not for mike - we need to initialize + // the mike on plugin + // this flag will be set when we have done the format setup + // so know if need to do it on play or not + // now need 2 flags - one for play and one for capture + int headset_play_format_setup_needed; + int headset_capture_format_setup_needed; + + int headset_presetup_done; + + + int use_data; + + + // this is new item for dealing with headset plugins + // so can distinguish which phase we are in if have multiple interrupts + // not really used now have analyzed interrupts properly + int headset_phase; + + // another dirty hack item to manage the different headset enable codes + int headset_enable; + + int play_init; + int capture_init; + + + // new item to limit times we redo unmute/play + struct timespec64 last_play_time; + // record the first play time - we have a problem there + // some initial plays that I dont understand - so skip any setup + // till sometime after the first play + struct timespec64 first_play_time; + + +#endif + + /* verb exec op override */ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res);