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"); }