214 lines
6.6 KiB
C
214 lines
6.6 KiB
C
|
/*
|
||
|
* hpd.h: hotplug detection declarations.
|
||
|
*
|
||
|
* Copyright (c) 2015-2018, NVIDIA CORPORATION, All rights reserved.
|
||
|
* Author: Animesh Kishore <ankishore@nvidia.com>
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#ifndef __DRIVERS_VIDEO_TEGRA_DC_HPD_H__
|
||
|
#define __DRIVERS_VIDEO_TEGRA_DC_HPD_H__
|
||
|
|
||
|
#include "edid.h"
|
||
|
|
||
|
struct tegra_hpd_ops {
|
||
|
/* custom init state machine */
|
||
|
void (*init)(void *drv_data);
|
||
|
|
||
|
/* return if hpd asserted/de-asserted */
|
||
|
bool (*get_hpd_state)(void *drv_data);
|
||
|
|
||
|
/*
|
||
|
* panel is disconnected here.
|
||
|
* Disable display and notify others.
|
||
|
* Userspace has not yet been notified.
|
||
|
*/
|
||
|
void (*disable)(void *drv_data);
|
||
|
|
||
|
/* prepare to initiate edid read tx */
|
||
|
bool (*edid_read_prepare)(void *drv_data);
|
||
|
|
||
|
/* edid is available but no notification sent yet. */
|
||
|
void (*edid_ready)(void *drv_data);
|
||
|
|
||
|
/*
|
||
|
* hpd dropped but came back again
|
||
|
* in < HPD_DROP_TIMEOUT_MS.
|
||
|
* Check for any edid change has been done.
|
||
|
*/
|
||
|
void (*edid_recheck)(void *drv_data);
|
||
|
|
||
|
/* filter modes not supported by host */
|
||
|
bool (*(*get_mode_filter)(void *drv_data))
|
||
|
(const struct tegra_dc *dc, struct fb_videomode *mode);
|
||
|
|
||
|
/* core func to read edid over host-sink connection */
|
||
|
i2c_transfer_func_t (*edid_read)(void *drv_data);
|
||
|
|
||
|
/*
|
||
|
* edid is available. Notification sent.
|
||
|
* Add more notification here.
|
||
|
*/
|
||
|
void (*edid_notify)(void *drv_data);
|
||
|
};
|
||
|
|
||
|
struct tegra_hpd_timer_data {
|
||
|
/* How long HPD must be HIGH to be a legitimate PLUG event */
|
||
|
u32 plug_stabilize_delay_us;
|
||
|
|
||
|
/* How long HPD must be LOW to be a legitimate UNPLUG event */
|
||
|
u32 unplug_stabilize_delay_us;
|
||
|
|
||
|
/* How long to wait for HPD to be re-asserted once it goes LOW */
|
||
|
u32 reassert_delay_us;
|
||
|
|
||
|
/* How long to wait before attempting to read the EDID */
|
||
|
u32 check_edid_delay_us;
|
||
|
|
||
|
/*
|
||
|
* If this flag is true, the HPD state machine will categorically reset
|
||
|
* itself if HPD comes back up within the reassert_delay_us period.
|
||
|
*
|
||
|
* If this flag is false and HPD comes back up within the
|
||
|
* reassert_delay_us period, the HPD state machine will only reset
|
||
|
* itself if the EDID has changed.
|
||
|
*/
|
||
|
bool reset_on_reassert;
|
||
|
|
||
|
/*
|
||
|
* If this flag is true, the HPD state machine will categorically reset
|
||
|
* itself if it receives a PLUG event while still enabled.
|
||
|
*
|
||
|
* If this flag is false, the state machine will ignore PLUG events that
|
||
|
* occur in the above scenario.
|
||
|
*/
|
||
|
bool reset_on_plug_bounce;
|
||
|
};
|
||
|
|
||
|
struct tegra_hpd_data {
|
||
|
struct delayed_work dwork;
|
||
|
int shutdown;
|
||
|
int state;
|
||
|
bool req_suspend; /* not sticky */
|
||
|
int pending_hpd_evt;
|
||
|
void *drv_data;
|
||
|
struct tegra_hpd_ops *ops;
|
||
|
struct tegra_hpd_timer_data timer_data;
|
||
|
|
||
|
struct fb_monspecs mon_spec;
|
||
|
|
||
|
struct tegra_edid *edid;
|
||
|
int edid_reads;
|
||
|
|
||
|
struct tegra_edid_hdmi_eld eld;
|
||
|
bool eld_retrieved;
|
||
|
bool hpd_resuming;
|
||
|
|
||
|
struct rt_mutex lock;
|
||
|
|
||
|
#ifdef CONFIG_SWITCH
|
||
|
struct switch_dev hpd_switch;
|
||
|
#endif
|
||
|
|
||
|
struct tegra_dc *dc;
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
/*
|
||
|
* The initial state for the state machine. When entering RESET, we
|
||
|
* shut down all output and then proceed to the CHECK_PLUG state after a
|
||
|
* short debounce delay.
|
||
|
*/
|
||
|
STATE_HPD_RESET = 0,
|
||
|
|
||
|
/*
|
||
|
* After the debounce delay, check the status of the HPD line. If its
|
||
|
* low, then the cable is unplugged and we go directly to DONE_DISABLED.
|
||
|
* If it is high, then the cable is plugged and we proceed to CHECK_EDID
|
||
|
* in order to read the EDID and figure out the next step.
|
||
|
*/
|
||
|
STATE_PLUG,
|
||
|
|
||
|
/*
|
||
|
* CHECK_EDID is the state we stay in attempting to read the EDID
|
||
|
* information after we check the plug state and discover that we are
|
||
|
* plugged in. If we max out our retries and fail to read the EDID, we
|
||
|
* move to DONE_DISABLED. If we successfully read the EDID, we move on
|
||
|
* to DONE_ENABLE, set an initial video mode, then signal to the high
|
||
|
* level that we are ready for final mode selection.
|
||
|
*/
|
||
|
STATE_CHECK_EDID,
|
||
|
|
||
|
/*
|
||
|
* DONE_DISABLED is the state we stay in after being reset and either
|
||
|
* discovering that no cable is plugged in or after we think a cable is
|
||
|
* plugged in but fail to read EDID.
|
||
|
*/
|
||
|
STATE_DONE_DISABLED,
|
||
|
|
||
|
/*
|
||
|
* DONE_ENABLED is the state we say in after being reset and disovering
|
||
|
* a valid EDID at the other end of a plugged cable.
|
||
|
*/
|
||
|
STATE_DONE_ENABLED,
|
||
|
|
||
|
/*
|
||
|
* Some sinks will drop HPD as soon as the TMDS signals start up. They
|
||
|
* will hold HPD low for about second and then re-assert it. If the
|
||
|
* source simply holds steady and does not disable the TMDS lines, the
|
||
|
* sink seems to accept the video mode after having gone out to lunch
|
||
|
* for a bit. This seems to be the behavior of various sources which
|
||
|
* work with panels like this, so it is the behavior we emulate here.
|
||
|
* If HPD drops while we are in DONE_ENABLED, set a timer for 1.5
|
||
|
* seconds and transition to WAIT_FOR_HPD_REASSERT. If HPD has not come
|
||
|
* back within this time limit, then go ahead and transition to RESET
|
||
|
* and shut the system down. If HPD does come back within this time
|
||
|
* limit, then check the EDID again. If it has not changed, then we
|
||
|
* assume that we are still hooked to the same panel and just go back to
|
||
|
* DONE_ENABLED. If the EDID fails to read or has changed, we
|
||
|
* transition to RESET and start the system over again.
|
||
|
*/
|
||
|
STATE_WAIT_FOR_HPD_REASSERT,
|
||
|
|
||
|
/*
|
||
|
* RECHECK_EDID is the state we stay in while attempting to re-read the
|
||
|
* EDID following an HPD drop and re-assert which occurs while we are in
|
||
|
* the DONE_ENABLED state. see HPD_STATE_DONE_WAIT_FOR_HPD_REASSERT
|
||
|
* for more details.
|
||
|
*/
|
||
|
STATE_RECHECK_EDID,
|
||
|
|
||
|
/*
|
||
|
* Initial state at boot that checks if HPD is already initialized
|
||
|
* by bootloader and not go to HPD_STATE_RESET which would disable
|
||
|
* HPD and cause blanking of the bootloader displayed image.
|
||
|
*/
|
||
|
STATE_INIT_FROM_BOOTLOADER,
|
||
|
|
||
|
/*
|
||
|
* STATE_COUNT must be the final state in the enum.
|
||
|
* 1) Do not add states after STATE_COUNT.
|
||
|
* 2) Do not assign explicit values to the states.
|
||
|
* 3) Do not reorder states in the list without reordering the dispatch
|
||
|
* table in hpd.c
|
||
|
*/
|
||
|
HPD_STATE_COUNT,
|
||
|
};
|
||
|
|
||
|
void tegra_hpd_init(struct tegra_hpd_data *data, struct tegra_dc *dc,
|
||
|
void *drv_data, struct tegra_hpd_ops *ops);
|
||
|
void tegra_hpd_shutdown(struct tegra_hpd_data *data);
|
||
|
void tegra_hpd_set_pending_evt(struct tegra_hpd_data *data);
|
||
|
int tegra_hpd_get_state(struct tegra_hpd_data *data);
|
||
|
void tegra_hpd_suspend(struct tegra_hpd_data *data);
|
||
|
|
||
|
#endif
|