/************************************************************
 *
 * 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);
	}
}
示例#2
0
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);
}
示例#5
0
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);
}