/* * cs8409_enable_i2c_clock - Disable I2C clocks * @codec: the codec instance * Disable I2C clocks. * This must be called when the i2c mutex is unlocked.
*/ staticvoid cs8409_disable_i2c_clock(struct hda_codec *codec)
{ struct cs8409_spec *spec = codec->spec;
/* * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use
*/ staticvoid cs8409_disable_i2c_clock_worker(struct work_struct *work)
{ struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work);
cs8409_disable_i2c_clock(spec->codec);
}
/* * cs8409_enable_i2c_clock - Enable I2C clocks * @codec: the codec instance * Enable I2C clocks. * This must be called when the i2c mutex is locked.
*/ staticvoid cs8409_enable_i2c_clock(struct hda_codec *codec)
{ struct cs8409_spec *spec = codec->spec;
/* Cancel the disable timer, but do not wait for any running disable functions to finish. * If the disable timer runs out before cancel, the delayed work thread will be blocked, * waiting for the mutex to become unlocked. This mutex will be locked for the duration of * any i2c transaction, so the disable function will run to completion immediately * afterwards in the scenario. The next enable call will re-enable the clock, regardless.
*/
cancel_delayed_work(&spec->i2c_clk_work);
if (cs8409_i2c_wait_complete(codec) < 0) goto error; /* Certain use cases may require a delay * after a write operation before proceeding.
*/ if (seq[i].delay)
fsleep(seq[i].delay);
}
for (i = 0; i < spec->num_scodecs; i++)
ur_gpios |= spec->scodecs[i]->irq_mask;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
flag ? ur_gpios : 0);
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
flag ? AC_UNSOL_ENABLED : 0);
}
staticvoid cs8409_fix_caps(struct hda_codec *codec, unsignedint nid)
{ int caps;
/* CS8409 is simple HDA bridge and intended to be used with a remote * companion codec. Most of input/output PIN(s) have only basic * capabilities. Receive and Transmit NID(s) have only OUTC and INC * capabilities and no presence detect capable (PDC) and call to * snd_hda_gen_build_controls() will mark them as non detectable * phantom jacks. However, a companion codec may be * connected to these pins which supports jack detect * capabilities. We have to override pin capabilities, * otherwise they will not be created as input devices.
*/
caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP); if (caps >= 0)
snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP,
(caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
case CS42L42_TS_UNPLUG:
status_changed = 1;
cs42l42->hp_jack_in = 0;
cs42l42->mic_jack_in = 0; break; default: /* jack in transition */ break;
}
codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
return status_changed;
}
staticint cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
{ int current_plug_status; int status_changed = 0; int reg_cdc_status; int reg_hs_status; int reg_ts_status; int type;
/* Read jack detect status registers */
reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
/* If status values are < 0, read error has occurred. */ if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0) return -EIO;
/* Clear interrupts, by reading interrupt status registers */
cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); if (cs42l42->full_scale_vol) { // Set the full scale volume bit
fsv |= CS42L42_FULL_SCALE_VOL_MASK;
cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
} // Unmute analog channels A and B
fsv = (fsv & ~CS42L42_ANA_MUTE_AB);
cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
/* we have to explicitly allow unsol event handling even during the * resume phase so that the jack event is processed properly
*/
snd_hda_codec_allow_unsol_events(cs42l42->codec);
/* Cancel i2c clock disable timer, and disable clock if left enabled */
cancel_delayed_work_sync(&spec->i2c_clk_work);
cs8409_disable_i2c_clock(codec);
/* * In the case of CS8409 we do not have unsolicited events from NID's 0x24 * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will * generate interrupt via gpio 4 to notify jack events. We have to overwrite * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers * and then notify status via generic snd_hda_jack_unsol_event() call.
*/ staticvoid cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsignedint res)
{ struct cs8409_spec *spec = codec->spec; struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0]; struct hda_jack_tbl *jk;
/* jack_unsol_event() will be called every time gpio line changing state. * In this case gpio4 line goes up as a result of reading interrupt status * registers in previous cs8409_jack_unsol_event() call. * We don't need to handle this event, ignoring...
*/ if (res & cs42l42->irq_mask) return;
if (cs42l42_jack_unsol_event(cs42l42)) {
snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
cs42l42->hp_jack_in ? 0 : PIN_OUT); /* Report jack*/
jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); if (jk)
snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG); /* Report jack*/
jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); if (jk)
snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
}
}
if (spec->unsol_event)
spec->unsol_event(codec, res); else
cs8409_cs42l42_jack_unsol_event(codec, res);
}
/* Manage PDREF, when transition to D3hot */ staticint cs8409_cs42l42_suspend(struct hda_codec *codec)
{ struct cs8409_spec *spec = codec->spec; int i;
spec->init_done = 0;
cs8409_enable_ur(codec, 0);
for (i = 0; i < spec->num_scodecs; i++)
cs42l42_suspend(spec->scodecs[i]);
/* Cancel i2c clock disable timer, and disable clock if left enabled */
cancel_delayed_work_sync(&spec->i2c_clk_work);
cs8409_disable_i2c_clock(codec);
/* CS8409 pins have no AC_PINSENSE_PRESENCE * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 * and return correct pin sense values for read_pin_sense() call from * hda_jack based on CS42L42 jack detect status.
*/ switch (nid) { case CS8409_CS42L42_HP_PIN_NID: if (verb == AC_VERB_GET_PIN_SENSE) {
*res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; return 0;
} break; case CS8409_CS42L42_AMIC_PIN_NID: if (verb == AC_VERB_GET_PIN_SENSE) {
*res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; return 0;
} break; default: break;
}
break; case HDA_FIXUP_ACT_PROBE: /* Fix Sample Rate to 48kHz */
spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; /* add hooks */
spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook; if (codec->fixup_id != CS8409_ODIN) /* Set initial DMIC volume to -26 dB */
snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
HDA_INPUT, 0, 0xff, 0x19);
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
&cs42l42_adc_volume_mixer); if (spec->speaker_pdn_gpio > 0)
snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
&cs8409_spk_sw_ctrl); /* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
snd_hda_codec_set_name(codec, "CS8409/CS42L42"); break; case HDA_FIXUP_ACT_INIT:
cs8409_cs42l42_hw_init(codec);
spec->init_done = 1; if (spec->init_done && spec->build_ctrl_done
&& !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); break; case HDA_FIXUP_ACT_BUILD:
spec->build_ctrl_done = 1; /* Run jack auto detect first time on boot * after controls have been added, to check if jack has * been already plugged in. * Run immediately after init.
*/ if (spec->init_done && spec->build_ctrl_done
&& !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]); break; default: break;
}
}
/****************************************************************************** * Dolphin Specific Functions * CS8409/ 2 X CS42L42
******************************************************************************/
/* * In the case of CS8409 we do not have unsolicited events when * hs mic and hp are connected. Companion codec CS42L42 will * generate interrupt via irq_mask to notify jack events. We have to overwrite * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers * and then notify status via generic snd_hda_jack_unsol_event() call.
*/ staticvoid dolphin_jack_unsol_event(struct hda_codec *codec, unsignedint res)
{ struct cs8409_spec *spec = codec->spec; struct sub_codec *cs42l42; struct hda_jack_tbl *jk;
cs42l42 = spec->scodecs[CS8409_CODEC0]; if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
cs42l42_jack_unsol_event(cs42l42)) {
jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0); if (jk)
snd_hda_jack_unsol_event(codec,
(jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0); if (jk)
snd_hda_jack_unsol_event(codec,
(jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
}
cs42l42 = spec->scodecs[CS8409_CODEC1]; if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
cs42l42_jack_unsol_event(cs42l42)) {
jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0); if (jk)
snd_hda_jack_unsol_event(codec,
(jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
AC_UNSOL_RES_TAG);
}
}
/* CS8409 pins have no AC_PINSENSE_PRESENCE * capabilities. We have to intercept calls for CS42L42 pins * and return correct pin sense values for read_pin_sense() call from * hda_jack based on CS42L42 jack detect status.
*/ switch (nid) { case DOLPHIN_HP_PIN_NID: case DOLPHIN_LO_PIN_NID: if (nid == DOLPHIN_LO_PIN_NID)
cs42l42 = spec->scodecs[CS8409_CODEC1]; if (verb == AC_VERB_GET_PIN_SENSE) {
*res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; return 0;
} break; case DOLPHIN_AMIC_PIN_NID: if (verb == AC_VERB_GET_PIN_SENSE) {
*res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; return 0;
} break; default: break;
}
return spec->exec_verb(dev, cmd, flags, res);
}
void dolphin_fixups(struct hda_codec *codec, conststruct hda_fixup *fix, int action)
{ struct cs8409_spec *spec = codec->spec; struct snd_kcontrol_new *kctrl; int i;
switch (action) { case HDA_FIXUP_ACT_PRE_PROBE:
snd_hda_add_verbs(codec, dolphin_init_verbs); /* verb exec op override */
spec->exec_verb = codec->core.exec_verb;
codec->core.exec_verb = dolphin_exec_verb;
break; case HDA_FIXUP_ACT_PROBE: /* Fix Sample Rate to 48kHz */
spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture; /* add hooks */
spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume",
&cs42l42_dac_volume_mixer); /* Update Line Out kcontrol template */ if (kctrl)
kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1,
HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE;
cs8409_enable_ur(codec, 0);
snd_hda_codec_set_name(codec, "CS8409/CS42L42"); break; case HDA_FIXUP_ACT_INIT:
dolphin_hw_init(codec);
spec->init_done = 1; if (spec->init_done && spec->build_ctrl_done) { for (i = 0; i < spec->num_scodecs; i++) { if (!spec->scodecs[i]->hp_jack_in)
cs42l42_run_jack_detect(spec->scodecs[i]);
}
} break; case HDA_FIXUP_ACT_BUILD:
spec->build_ctrl_done = 1; /* Run jack auto detect first time on boot * after controls have been added, to check if jack has * been already plugged in. * Run immediately after init.
*/ if (spec->init_done && spec->build_ctrl_done) { for (i = 0; i < spec->num_scodecs; i++) { if (!spec->scodecs[i]->hp_jack_in)
cs42l42_run_jack_detect(spec->scodecs[i]);
}
} break; default: break;
}
}
staticint cs8409_probe(struct hda_codec *codec, conststruct hda_device_id *id)
{ int err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.