/************************************************************ * * main state machine worker function * ************************************************************/ static void hdmi_state_machine_worker(struct work_struct *work) { int pending_hpd_evt, cur_hpd; /* Observe and clear the pending flag and latch the current HPD state. */ rt_mutex_lock(&work_lock); pending_hpd_evt = work_state.pending_hpd_evt; work_state.pending_hpd_evt = 0; cur_hpd = tegra_dc_hpd(work_state.hdmi->dc); rt_mutex_unlock(&work_lock); pr_info("%s (tid %p): state %d (%s), hpd %d, pending_hpd_evt %d\n", __func__, current, work_state.state, state_names[work_state.state], cur_hpd, pending_hpd_evt); if (pending_hpd_evt) { /* If we were woken up because of HPD activity, just schedule * the next appropriate task and get out. */ hdmi_state_machine_handle_hpd_l(cur_hpd); } else if (work_state.state < ARRAY_SIZE(state_machine_dispatch)) { dispatch_func_t func = state_machine_dispatch[work_state.state]; if (NULL == func) pr_warn("NULL state machine handler while in state %d; how did we end up here?", work_state.state); else func(work_state.hdmi); } else { pr_warn("hdmi state machine worker scheduled unexpected state %d", work_state.state); } }
static bool _tegra_dc_enable(struct tegra_dc *dc) { if (dc->mode.pclk == 0) { switch (dc->out->type) { case TEGRA_DC_OUT_HDMI: /* DC enable called but no videomode is loaded. Check if HDMI is connected, then set fallback mdoe */ if (tegra_dc_hpd(dc)) { if (_tegra_dc_set_default_videomode(dc)) return false; } else return false; break; /* Do nothing for other outputs for now */ case TEGRA_DC_OUT_RGB: case TEGRA_DC_OUT_DSI: default: return false; } } if (!dc->out) return false; tegra_dc_io_start(dc); return _tegra_dc_controller_enable(dc); }
static void handle_check_plug_state_l(struct tegra_dc_hdmi_data *hdmi) { if (tegra_dc_hpd(work_state.hdmi->dc)) { /* Looks like there is something plugged in. * Get ready to read the sink's EDID information. */ work_state.edid_reads = 0; hdmi_state_machine_set_state_l(HDMI_STATE_CHECK_EDID, CHECK_EDID_DELAY_MS); } else { /* nothing plugged in, so we are finished. Go to the * DONE_DISABLED state and stay there until the next HPD event. * */ hdmi_disable_l(hdmi, true); hdmi_state_machine_set_state_l(HDMI_STATE_DONE_DISABLED, -1); } }
static void handle_check_edid_l(struct tegra_dc_hdmi_data *hdmi) { struct fb_monspecs specs; #ifdef CONFIG_SWITCH int state; #endif memset(&specs, 0, sizeof(specs)); #ifdef CONFIG_FRAMEBUFFER_CONSOLE /* Set default videomode on dc before enabling it*/ tegra_dc_set_default_videomode(hdmi->dc); #endif if (!tegra_dc_hpd(work_state.hdmi->dc)) { /* hpd dropped - stop EDID read */ pr_info("hpd == 0, aborting EDID read\n"); goto end_disabled; } if (tegra_edid_get_monspecs(hdmi->edid, &specs)) { /* Failed to read EDID. If we still have retry attempts left, * schedule another attempt. Otherwise give up and just go to * the disabled state. */ work_state.edid_reads++; if (work_state.edid_reads >= MAX_EDID_READ_ATTEMPTS) { pr_info("Failed to read EDID after %d times. Giving up.\n", work_state.edid_reads); goto end_disabled; } else { hdmi_state_machine_set_state_l(HDMI_STATE_CHECK_EDID, CHECK_EDID_DELAY_MS); } return; } if (tegra_edid_get_eld(hdmi->edid, &hdmi->eld) < 0) { pr_err("error populating eld\n"); goto end_disabled; } hdmi->eld_retrieved = true; pr_info("panel size %d by %d\n", specs.max_x, specs.max_y); /* monitors like to lie about these but they are still useful for * detecting aspect ratios */ hdmi->dc->out->h_size = specs.max_x * 1000; hdmi->dc->out->v_size = specs.max_y * 1000; hdmi->dvi = !(specs.misc & FB_MISC_HDMI); /* Need to unpowergate DC if it was powergated. Updating monitorspecs * triggers pan_display which tries updating windows */ if (hdmi->dc->enabled && !tegra_dc_is_powered(hdmi->dc)) tegra_dc_unpowergate_locked(hdmi->dc); tegra_fb_update_monspecs(hdmi->dc->fb, &specs, tegra_dc_hdmi_mode_filter); #ifdef CONFIG_SWITCH state = tegra_edid_audio_supported(hdmi->edid) ? 1 : 0; switch_set_state(&hdmi->audio_switch, state); pr_info("%s: audio_switch %d\n", __func__, state); switch_set_state(&hdmi->hpd_switch, 1); pr_info("Display connected, hpd_switch 1\n"); #endif hdmi->dc->connected = true; tegra_dc_ext_process_hotplug(hdmi->dc->ndev->id); hdmi_state_machine_set_state_l(HDMI_STATE_DONE_ENABLED, 0); return; end_disabled: hdmi->eld_retrieved = false; hdmi_disable_l(hdmi, true); hdmi_state_machine_set_state_l(HDMI_STATE_DONE_DISABLED, -1); }
static void handle_check_edid_l(struct tegra_dc_hdmi_data *hdmi) { struct fb_monspecs specs; #ifdef CONFIG_SWITCH int state; #endif memset(&specs, 0, sizeof(specs)); #ifdef CONFIG_FRAMEBUFFER_CONSOLE /* Set default videomode on dc before enabling it*/ tegra_dc_set_default_videomode(hdmi->dc); #endif if (!tegra_dc_hpd(work_state.hdmi->dc)) { /* hpd dropped - stop EDID read */ pr_info("hpd == 0, aborting EDID read\n"); goto end_disabled; } if (tegra_edid_get_monspecs(hdmi->edid, &specs)) { /* Failed to read EDID. If we still have retry attempts left, * schedule another attempt. Otherwise give up and just go to * the disabled state. */ work_state.edid_reads++; if (work_state.edid_reads >= MAX_EDID_READ_ATTEMPTS) { pr_info("Failed to read EDID after %d times. Giving up.\n", work_state.edid_reads); goto end_disabled; } else { hdmi_state_machine_set_state_l(HDMI_STATE_CHECK_EDID, CHECK_EDID_DELAY_MS); } return; } if (tegra_edid_get_eld(hdmi->edid, &hdmi->eld) < 0) { pr_err("error populating eld\n"); goto end_disabled; } hdmi->eld_retrieved = true; pr_info("panel size %d by %d\n", specs.max_x, specs.max_y); /* monitors like to lie about these but they are still useful for * detecting aspect ratios */ hdmi->dc->out->h_size = specs.max_x * 1000; hdmi->dc->out->v_size = specs.max_y * 1000; hdmi->dvi = !(specs.misc & FB_MISC_HDMI); #ifdef CONFIG_ADF_TEGRA tegra_adf_process_hotplug_connected(hdmi->dc->adf, &specs); #endif #ifdef CONFIG_TEGRA_DC_EXTENSIONS tegra_fb_update_monspecs(hdmi->dc->fb, &specs, tegra_dc_hdmi_mode_filter); #endif #ifdef CONFIG_SWITCH state = tegra_edid_audio_supported(hdmi->edid) ? 1 : 0; switch_set_state(&hdmi->audio_switch, state); pr_info("%s: audio_switch %d\n", __func__, state); switch_set_state(&hdmi->hpd_switch, 1); pr_info("Display connected, hpd_switch 1\n"); #endif hdmi->dc->connected = true; #ifdef CONFIG_TEGRA_DC_EXTENSIONS tegra_dc_ext_process_hotplug(hdmi->dc->ndev->id); #endif if (unlikely(tegra_is_clk_enabled(hdmi->clk))) { /* the only time this should happen is on boot, where the * sequence is that hdmi is enabled before EDID is read. * hdmi_enable() doesn't have EDID information yet so can't * setup audio and infoframes, so we have to do so here. */ pr_info("%s: setting audio and infoframes\n", __func__); tegra_dc_io_start(hdmi->dc); tegra_dc_hdmi_setup_audio_and_infoframes(hdmi->dc); tegra_dc_io_end(hdmi->dc); } hdmi_state_machine_set_state_l(HDMI_STATE_DONE_ENABLED, -1); return; end_disabled: hdmi->eld_retrieved = false; hdmi_disable_l(hdmi); hdmi_state_machine_set_state_l(HDMI_STATE_DONE_DISABLED, -1); }