/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
 */

#ifndef __SDE_ENCODER_PHYS_H__
#define __SDE_ENCODER_PHYS_H__

#include <linux/jiffies.h>
#include <linux/sde_rsc.h>

#include "sde_kms.h"
#include "sde_hw_intf.h"
#include "sde_hw_pingpong.h"
#include "sde_hw_ctl.h"
#include "sde_hw_top.h"
#include "sde_hw_wb.h"
#include "sde_hw_cdm.h"
#include "sde_hw_dnsc_blur.h"
#include "sde_encoder.h"
#include "sde_connector.h"

#define SDE_ENCODER_NAME_MAX	16

/* wait for at most 2 vsync for lowest refresh rate (24hz) */
#define DEFAULT_KICKOFF_TIMEOUT_MS		84

/* wait 1 sec for the emulated targets */
#define MAX_KICKOFF_TIMEOUT_MS                  100000

#define MAX_TE_PROFILE_COUNT		5
/**
 * enum sde_enc_split_role - Role this physical encoder will play in a
 *	split-panel configuration, where one panel is master, and others slaves.
 *	Masters have extra responsibilities, like managing the VBLANK IRQ.
 * @ENC_ROLE_SOLO:	This is the one and only panel. This encoder is master.
 * @ENC_ROLE_MASTER:	This encoder is the master of a split panel config.
 * @ENC_ROLE_SLAVE:	This encoder is not the master of a split panel config.
 * @ENC_ROLE_SKIP:	This encoder is not participating in kickoffs
 */
enum sde_enc_split_role {
	ENC_ROLE_SOLO,
	ENC_ROLE_MASTER,
	ENC_ROLE_SLAVE,
	ENC_ROLE_SKIP
};

/**
 * enum sde_enc_enable_state - current enabled state of the physical encoder
 * @SDE_ENC_DISABLING:	Encoder transitioning to disable state
 *			Events bounding transition are encoder type specific
 * @SDE_ENC_DISABLED:	Encoder is disabled
 * @SDE_ENC_ENABLING:	Encoder transitioning to enabled
 *			Events bounding transition are encoder type specific
 * @SDE_ENC_ENABLED:	Encoder is enabled
 * @SDE_ENC_ERR_NEEDS_HW_RESET:	Encoder is enabled, but requires a hw_reset
 *				to recover from a previous error
 */
enum sde_enc_enable_state {
	SDE_ENC_DISABLING,
	SDE_ENC_DISABLED,
	SDE_ENC_ENABLING,
	SDE_ENC_ENABLED,
	SDE_ENC_ERR_NEEDS_HW_RESET
};

struct sde_encoder_phys;

/**
 * struct sde_encoder_virt_ops - Interface the containing virtual encoder
 *	provides for the physical encoders to use to callback.
 * @handle_vblank_virt:	Notify virtual encoder of vblank IRQ reception
 *			Note: This is called from IRQ handler context.
 * @handle_underrun_virt: Notify virtual encoder of underrun IRQ reception
 *			Note: This is called from IRQ handler context.
 * @handle_frame_done:	Notify virtual encoder that this phys encoder
 *			completes last request frame.
 * @get_qsync_fps:	Returns the min fps for the qsync feature.
 */
struct sde_encoder_virt_ops {
	void (*handle_vblank_virt)(struct drm_encoder *parent,
			struct sde_encoder_phys *phys);
	void (*handle_underrun_virt)(struct drm_encoder *parent,
			struct sde_encoder_phys *phys);
	void (*handle_frame_done)(struct drm_encoder *parent,
			struct sde_encoder_phys *phys, u32 event);
	void (*get_qsync_fps)(struct drm_encoder *parent,
			u32 *qsync_fps, struct drm_connector_state *conn_state);
};

/**
 * struct sde_encoder_phys_ops - Interface the physical encoders provide to
 *	the containing virtual encoder.
 * @late_register:		DRM Call. Add Userspace interfaces, debugfs.
 * @prepare_commit:		MSM Atomic Call, start of atomic commit sequence
 * @is_master:			Whether this phys_enc is the current master
 *				encoder. Can be switched at enable time. Based
 *				on split_role and current mode (CMD/VID).
 * @mode_fixup:			DRM Call. Fixup a DRM mode.
 * @cont_splash_mode_set:	mode set with specific HW resources during
 *                              cont splash enabled state.
 * @mode_set:			DRM Call. Set a DRM mode.
 *				This likely caches the mode, for use at enable.
 * @enable:			DRM Call. Enable a DRM mode.
 * @disable:			DRM Call. Disable mode.
 * @atomic_check:		DRM Call. Atomic check new DRM state.
 * @destroy:			DRM Call. Destroy and release resources.
 * @get_hw_resources:		Populate the structure with the hardware
 *				resources that this phys_enc is using.
 *				Expect no overlap between phys_encs.
 * @control_vblank_irq		Register/Deregister for VBLANK IRQ
 * @wait_for_commit_done:	Wait for hardware to have flushed the
 *				current pending frames to hardware
 * @wait_for_tx_complete:	Wait for hardware to transfer the pixels
 *				to the panel
 * @wait_for_vblank:		Wait for VBLANK, for sub-driver internal use
 * @prepare_for_kickoff:	Do any work necessary prior to a kickoff
 *				For CMD encoder, may wait for previous tx done
 * @handle_post_kickoff:	Do any work necessary post-kickoff work
 * @trigger_flush:		Process flush event on physical encoder
 * @trigger_start:		Process start event on physical encoder
 * @needs_single_flush:		Whether encoder slaves need to be flushed
 * @setup_misr:		Sets up MISR, enable and disables based on sysfs
 * @collect_misr:		Collects MISR data on frame update
 * @hw_reset:			Issue HW recovery such as CTL reset and clear
 *				SDE_ENC_ERR_NEEDS_HW_RESET state
 * @irq_control:		Handler to enable/disable all the encoder IRQs
 * @update_split_role:		Update the split role of the phys enc
 * @control_te:			Interface to control the vsync_enable status
 * @restore:			Restore all the encoder configs.
 * @is_autorefresh_enabled:	provides the autorefresh current
 *                              enable/disable state.
 * @get_line_count:		Obtain current internal vertical line count
 * @wait_dma_trigger:		Returns true if lut dma has to trigger and wait
 *                              unitl transaction is complete.
 * @wait_for_active:		Wait for display scan line to be in active area
 * @setup_vsync_source:		Configure vsync source selection for cmd mode.
 * @get_underrun_line_count:	Obtain and log current internal vertical line
 *                              count and underrun line count
 * @add_to_minidump:		Add this phys_enc data to minidumps
 * @disable_autorefresh:	Disable autorefresh
 */

struct sde_encoder_phys_ops {
	int (*late_register)(struct sde_encoder_phys *encoder,
			struct dentry *debugfs_root);
	void (*prepare_commit)(struct sde_encoder_phys *encoder);
	bool (*is_master)(struct sde_encoder_phys *encoder);
	bool (*mode_fixup)(struct sde_encoder_phys *encoder,
			const struct drm_display_mode *mode,
			struct drm_display_mode *adjusted_mode);
	void (*mode_set)(struct sde_encoder_phys *encoder,
			struct drm_display_mode *mode,
			struct drm_display_mode *adjusted_mode, bool *reinit_mixers);
	void (*cont_splash_mode_set)(struct sde_encoder_phys *encoder,
			struct drm_display_mode *adjusted_mode);
	void (*enable)(struct sde_encoder_phys *encoder);
	void (*disable)(struct sde_encoder_phys *encoder);
	int (*atomic_check)(struct sde_encoder_phys *encoder,
			    struct drm_crtc_state *crtc_state,
			    struct drm_connector_state *conn_state);
	void (*destroy)(struct sde_encoder_phys *encoder);
	void (*get_hw_resources)(struct sde_encoder_phys *encoder,
			struct sde_encoder_hw_resources *hw_res,
			struct drm_connector_state *conn_state);
	int (*control_vblank_irq)(struct sde_encoder_phys *enc, bool enable);
	int (*wait_for_commit_done)(struct sde_encoder_phys *phys_enc);
	int (*wait_for_tx_complete)(struct sde_encoder_phys *phys_enc);
	int (*wait_for_vblank)(struct sde_encoder_phys *phys_enc);
	int (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
			struct sde_encoder_kickoff_params *params);
	void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
	void (*trigger_flush)(struct sde_encoder_phys *phys_enc);
	void (*trigger_start)(struct sde_encoder_phys *phys_enc);
	bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc);

	void (*setup_misr)(struct sde_encoder_phys *phys_encs,
				bool enable, u32 frame_count);
	int (*collect_misr)(struct sde_encoder_phys *phys_enc, bool nonblock,
			u32 *misr_value);
	void (*hw_reset)(struct sde_encoder_phys *phys_enc);
	void (*irq_control)(struct sde_encoder_phys *phys, bool enable);
	void (*update_split_role)(struct sde_encoder_phys *phys_enc,
			enum sde_enc_split_role role);
	void (*control_te)(struct sde_encoder_phys *phys_enc, bool enable);
	void (*restore)(struct sde_encoder_phys *phys);
	bool (*is_autorefresh_enabled)(struct sde_encoder_phys *phys);
	int (*get_line_count)(struct sde_encoder_phys *phys);
	bool (*wait_dma_trigger)(struct sde_encoder_phys *phys);
	int (*wait_for_active)(struct sde_encoder_phys *phys);
	void (*setup_vsync_source)(struct sde_encoder_phys *phys, u32 vsync_source,
			struct msm_display_info *disp_info);
	u32 (*get_underrun_line_count)(struct sde_encoder_phys *phys);
	void (*add_to_minidump)(struct sde_encoder_phys *phys);
	void (*disable_autorefresh)(struct sde_encoder_phys *phys);
};

/**
 * enum sde_intr_idx - sde encoder interrupt index
 * @INTR_IDX_VSYNC:    Vsync interrupt for video mode panel
 * @INTR_IDX_PINGPONG: Pingpong done interrupt for cmd mode panel
 * @INTR_IDX_UNDERRUN: Underrun interrupt for video and cmd mode panel
 * @INTR_IDX_CTL_START:Control start interrupt to indicate the frame start
 * @INTR_IDX_CTL_DONE: Control done interrupt indicating the control path being idle
 * @INTR_IDX_RDPTR:    Readpointer done interrupt for cmd mode panel
 * @INTR_IDX_WB_DONE:  Writeback done interrupt for WB
 * @INTR_IDX_PP1_OVFL: Pingpong overflow interrupt on PP1 for Concurrent WB
 * @INTR_IDX_PP2_OVFL: Pingpong overflow interrupt on PP2 for Concurrent WB
 * @INTR_IDX_PP3_OVFL: Pingpong overflow interrupt on PP3 for Concurrent WB
 * @INTR_IDX_PP4_OVFL: Pingpong overflow interrupt on PP4 for Concurrent WB
 * @INTR_IDX_PP5_OVFL: Pingpong overflow interrupt on PP5 for Concurrent WB
 * @INTR_IDX_PP_CWB_OVFL: Pingpong overflow interrupt on PP_CWB0/1 for Concurrent WB
 * @INTR_IDX_AUTOREFRESH_DONE:  Autorefresh done for cmd mode panel meaning
 *                              autorefresh has triggered a double buffer flip
 * @INTR_IDX_WRPTR:    Writepointer start interrupt for cmd mode panel
 * @INTR_IDX_WB_LINEPTR:  Programmable lineptr interrupt for WB
 */
enum sde_intr_idx {
	INTR_IDX_VSYNC,
	INTR_IDX_PINGPONG,
	INTR_IDX_UNDERRUN,
	INTR_IDX_CTL_START,
	INTR_IDX_CTL_DONE,
	INTR_IDX_RDPTR,
	INTR_IDX_AUTOREFRESH_DONE,
	INTR_IDX_WB_DONE,
	INTR_IDX_PP1_OVFL,
	INTR_IDX_PP2_OVFL,
	INTR_IDX_PP3_OVFL,
	INTR_IDX_PP4_OVFL,
	INTR_IDX_PP5_OVFL,
	INTR_IDX_PP_CWB_OVFL,
	INTR_IDX_WRPTR,
	INTR_IDX_WB_LINEPTR,
	INTR_IDX_MAX,
};

/**
 * sde_encoder_irq - tracking structure for interrupts
 * @name:		string name of interrupt
 * @intr_type:		Encoder interrupt type
 * @intr_idx:		Encoder interrupt enumeration
 * @hw_idx:		HW Block ID
 * @irq_idx:		IRQ interface lookup index from SDE IRQ framework
 *			will be -EINVAL if IRQ is not registered
 * @irq_cb:		interrupt callback
 */
struct sde_encoder_irq {
	const char *name;
	enum sde_intr_type intr_type;
	enum sde_intr_idx intr_idx;
	int hw_idx;
	int irq_idx;
	struct sde_irq_callback cb;
};

/**
 * struct sde_encoder_phys - physical encoder that drives a single INTF block
 *	tied to a specific panel / sub-panel. Abstract type, sub-classed by
 *	phys_vid or phys_cmd for video mode or command mode encs respectively.
 * @parent:		Pointer to the containing virtual encoder
 * @connector:		If a mode is set, cached pointer to the active connector
 * @ops:		Operations exposed to the virtual encoder
 * @parent_ops:		Callbacks exposed by the parent to the phys_enc
 * @hw_mdptop:		Hardware interface to the top registers
 * @hw_ctl:		Hardware interface to the ctl registers
 * @hw_intf:		Hardware interface to INTF registers
 * @hw_cdm:		Hardware interface to the cdm registers
 * @hw_qdss:		Hardware interface to the qdss registers
 * @cdm_cfg:		Chroma-down hardware configuration
 * @hw_pp:		Hardware interface to the ping pong registers
 * @hw_dnsc_blur:	Hardware interface to the downscale blur registers
 * @sde_kms:		Pointer to the sde_kms top level
 * @cached_mode:	DRM mode cached at mode_set time, acted on in enable
 * @enabled:		Whether the encoder has enabled and running a mode
 * @split_role:		Role to play in a split-panel configuration
 * @intf_mode:		Interface mode
 * @intf_idx:		Interface index on sde hardware
 * @intf_cfg:		Interface hardware configuration
 * @intf_cfg_v1:        Interface hardware configuration to be used if control
 *                      path supports SDE_CTL_ACTIVE_CFG
 * @comp_type:      Type of compression supported
 * @comp_ratio:		Compression ratio
 * @dsc_extra_pclk_cycle_cnt: Extra pclk cycle count for DSC over DP
 * @dsc_extra_disp_width: Additional display width for DSC over DP
 * @poms_align_vsync:   poms with vsync aligned
 * @dce_bytes_per_line:	Compressed bytes per line
 * @enc_spinlock:	Virtual-Encoder-Wide Spin Lock for IRQ purposes
 * @enable_state:	Enable state tracking
 * @vblank_refcount:	Reference count of vblank request
 * @wbirq_refcount:	Reference count of wb irq request
 * @vsync_cnt:		Vsync count for the physical encoder
 * @last_vsync_timestamp:	store last vsync timestamp
 * @underrun_cnt:	Underrun count for the physical encoder
 * @pending_kickoff_cnt:	Atomic counter tracking the number of kickoffs
 *				vs. the number of done/vblank irqs. Should hover
 *				between 0-2 Incremented when a new kickoff is
 *				scheduled. Decremented in irq handler
 * @pending_retire_fence_cnt:   Atomic counter tracking the pending retire
 *                              fences that have to be signalled.
 * @pending_ctl_start_cnt:      Atomic counter tracking the pending ctl-start-irq,
 *                              used to release commit thread. Currently managed
 *                              only for writeback encoder and the counter keeps
 *                              increasing for other type of encoders.
 * @pending_kickoff_wq:		Wait queue for blocking until kickoff completes
 * @kickoff_timeout_ms:		kickoff timeout in mill seconds
 * @irq:			IRQ tracking structures
 * @has_intf_te:		Interface TE configuration support
 * @cont_splash_enabled:	Variable to store continuous splash settings.
 * @in_clone_mode		Indicates if encoder is in clone mode ref@CWB
 * @vfp_cached:			cached vertical front porch to be used for
 *				programming ROT and MDP fetch start
 * @frame_trigger_mode:		frame trigger mode indication for command
 *				mode display
 * @recovered:			flag set to true when recovered from pp timeout
 * @autorefresh_disable_trans:   flag set to true during autorefresh disable transition
 */
struct sde_encoder_phys {
	struct drm_encoder *parent;
	struct drm_connector *connector;
	struct sde_encoder_phys_ops ops;
	struct sde_encoder_virt_ops parent_ops;
	struct sde_hw_mdp *hw_mdptop;
	struct sde_hw_ctl *hw_ctl;
	struct sde_hw_intf *hw_intf;
	struct sde_hw_cdm *hw_cdm;
	struct sde_hw_qdss *hw_qdss;
	struct sde_hw_cdm_cfg cdm_cfg;
	struct sde_hw_pingpong *hw_pp;
	struct sde_hw_dnsc_blur *hw_dnsc_blur;
	struct sde_kms *sde_kms;
	struct drm_display_mode cached_mode;
	enum sde_enc_split_role split_role;
	enum sde_intf_mode intf_mode;
	enum sde_intf intf_idx;
	struct sde_hw_intf_cfg intf_cfg;
	struct sde_hw_intf_cfg_v1 intf_cfg_v1;
	enum msm_display_compression_type comp_type;
	u32 comp_ratio;
	u32 dsc_extra_pclk_cycle_cnt;
	u32 dsc_extra_disp_width;
	bool poms_align_vsync;
	u32 dce_bytes_per_line;
	spinlock_t *enc_spinlock;
	enum sde_enc_enable_state enable_state;
	struct mutex *vblank_ctl_lock;
	atomic_t vblank_refcount;
	atomic_t wbirq_refcount;
	atomic_t vsync_cnt;
	ktime_t last_vsync_timestamp;
	atomic_t underrun_cnt;
	atomic_t pending_kickoff_cnt;
	atomic_t pending_retire_fence_cnt;
	atomic_t pending_ctl_start_cnt;
	wait_queue_head_t pending_kickoff_wq;
	u32 kickoff_timeout_ms;
	struct sde_encoder_irq irq[INTR_IDX_MAX];
	bool has_intf_te;
	bool cont_splash_enabled;
	bool in_clone_mode;
	int vfp_cached;
	enum frame_trigger_mode_type frame_trigger_mode;
	bool recovered;
	bool autorefresh_disable_trans;
};

static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
{
	return atomic_inc_return(&phys->pending_kickoff_cnt);
}

/**
 * struct sde_encoder_phys_vid - sub-class of sde_encoder_phys to handle video
 *	mode specific operations
 * @base:	Baseclass physical encoder structure
 * @timing_params: Current timing parameter
 * @error_count: Number of consecutive kickoffs that experienced an error
 */
struct sde_encoder_phys_vid {
	struct sde_encoder_phys base;
	struct intf_timing_params timing_params;
	int error_count;
};

/**
 * struct sde_encoder_phys_cmd_autorefresh - autorefresh state tracking
 * @cfg: current active autorefresh configuration
 * @kickoff_cnt: atomic count tracking autorefresh done irq kickoffs pending
 * @kickoff_wq:	wait queue for waiting on autorefresh done irq
 */
struct sde_encoder_phys_cmd_autorefresh {
	struct sde_hw_autorefresh cfg;
	atomic_t kickoff_cnt;
	wait_queue_head_t kickoff_wq;
};

/**
 * struct sde_encoder_phys_cmd_te_timestamp - list node to keep track of
 *     rd_ptr/TE timestamp
 * @list: list node
 * @timestamp: TE timestamp
 */
struct sde_encoder_phys_cmd_te_timestamp {
	struct list_head list;
	ktime_t timestamp;
};

/**
 * struct sde_encoder_phys_cmd - sub-class of sde_encoder_phys to handle command
 *	mode specific operations
 * @base:	Baseclass physical encoder structure
 * @stream_sel:	Stream selection for multi-stream interfaces
 * @frame_tx_timeout_report_cnt: number of pp_done/ctl_done irq timeout errors
 * @autorefresh: autorefresh feature state
 * @pending_vblank_cnt: Atomic counter tracking pending wait for VBLANK
 * @pending_vblank_wq: Wait queue for blocking until VBLANK received
 * @wr_ptr_wait_success: log wr_ptr_wait success for release fence trigger
 * @te_timestamp_list: List head for the TE timestamp list
 * @te_timestamp: Array of size MAX_TE_PROFILE_COUNT te_timestamp_list elements
 * @qsync_threshold_lines: tearcheck threshold lines calculated based on qsync_min_fps
 */
struct sde_encoder_phys_cmd {
	struct sde_encoder_phys base;
	int stream_sel;
	int frame_tx_timeout_report_cnt;
	struct sde_encoder_phys_cmd_autorefresh autorefresh;
	atomic_t pending_vblank_cnt;
	wait_queue_head_t pending_vblank_wq;
	bool wr_ptr_wait_success;
	struct list_head te_timestamp_list;
	struct sde_encoder_phys_cmd_te_timestamp
			te_timestamp[MAX_TE_PROFILE_COUNT];
	u32 qsync_threshold_lines;
};

/**
 * struct sde_encoder_phys_wb - sub-class of sde_encoder_phys to handle
 *	writeback specific operations
 * @base:		Baseclass physical encoder structure
 * @hw_wb:		Hardware interface to the wb registers
 * @wbdone_timeout:	Timeout value for writeback done in msec
 * @wb_cfg:		Writeback hardware configuration
 * @cdp_cfg:		Writeback CDP configuration
 * @wb_roi:		Writeback region-of-interest
 * @wb_fmt:		Writeback pixel format
 * @wb_fb:		Pointer to current writeback framebuffer
 * @wb_aspace:		Pointer to current writeback address space
 * @old_fb:		Pointer to old writeback framebuffer
 * @old_aspace:		Pointer to old writeback address space
 * @aspace:		address space identifier for non-secure/secure domain
 * @wb_dev:		Pointer to writeback device
 * @bo_disable:		Buffer object(s) to use during the disabling state
 * @fb_disable:		Frame buffer to use during the disabling state
 * @sc_cfg:		Stores wb system cache config
 * @crtc:		Pointer to drm_crtc
 * @prog_line:		Cached programmable line value used to trigger early wb-fence
 */
struct sde_encoder_phys_wb {
	struct sde_encoder_phys base;
	struct sde_hw_wb *hw_wb;
	u32 wbdone_timeout;
	struct sde_hw_wb_cfg wb_cfg;
	struct sde_hw_wb_cdp_cfg cdp_cfg;
	struct sde_rect wb_roi;
	const struct sde_format *wb_fmt;
	struct drm_framebuffer *wb_fb;
	struct msm_gem_address_space *wb_aspace;
	struct drm_framebuffer *old_fb;
	struct msm_gem_address_space *old_aspace;
	struct msm_gem_address_space *aspace[SDE_IOMMU_DOMAIN_MAX];
	struct sde_wb_device *wb_dev;
	struct drm_gem_object *bo_disable[SDE_MAX_PLANES];
	struct drm_framebuffer *fb_disable;
	struct sde_hw_wb_sc_cfg sc_cfg;
	struct drm_crtc *crtc;
	u32 prog_line;
};

/**
 * struct sde_enc_phys_init_params - initialization parameters for phys encs
 * @sde_kms:		Pointer to the sde_kms top level
 * @parent:		Pointer to the containing virtual encoder
 * @parent_ops:		Callbacks exposed by the parent to the phys_enc
 * @split_role:		Role to play in a split-panel configuration
 * @intf_idx:		Interface index this phys_enc will control
 * @wb_idx:		Writeback index this phys_enc will control
 * @comp_type:      Type of compression supported
 * @enc_spinlock:	Virtual-Encoder-Wide Spin Lock for IRQ purposes
 */
struct sde_enc_phys_init_params {
	struct sde_kms *sde_kms;
	struct drm_encoder *parent;
	struct sde_encoder_virt_ops parent_ops;
	enum sde_enc_split_role split_role;
	enum sde_intf intf_idx;
	enum sde_wb wb_idx;
	enum msm_display_compression_type comp_type;
	spinlock_t *enc_spinlock;
	struct mutex *vblank_ctl_lock;
};

/**
 * sde_encoder_wait_info - container for passing arguments to irq wait functions
 * @wq: wait queue structure
 * @atomic_cnt: wait until atomic_cnt equals zero
 * @count_check: wait for specific atomic_cnt instead of zero.
 * @timeout_ms: timeout value in milliseconds
 */
struct sde_encoder_wait_info {
	wait_queue_head_t *wq;
	atomic_t *atomic_cnt;
	u32 count_check;
	s64 timeout_ms;
};

/**
 * sde_encoder_phys_vid_init - Construct a new video mode physical encoder
 * @p:	Pointer to init params structure
 * Return: Error code or newly allocated encoder
 */
struct sde_encoder_phys *sde_encoder_phys_vid_init(
		struct sde_enc_phys_init_params *p);

/**
 * sde_encoder_phys_cmd_init - Construct a new command mode physical encoder
 * @p:	Pointer to init params structure
 * Return: Error code or newly allocated encoder
 */
struct sde_encoder_phys *sde_encoder_phys_cmd_init(
		struct sde_enc_phys_init_params *p);

/**
 * sde_encoder_phys_wb_init - Construct a new writeback physical encoder
 * @p:	Pointer to init params structure
 * Return: Error code or newly allocated encoder
 */
#if IS_ENABLED(CONFIG_DRM_SDE_WB)
struct sde_encoder_phys *sde_encoder_phys_wb_init(
		struct sde_enc_phys_init_params *p);
#else
static inline
struct sde_encoder_phys *sde_encoder_phys_wb_init(
		struct sde_enc_phys_init_params *p)
{
	return NULL;
}
#endif /* CONFIG_DRM_SDE_WB */

void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb, const struct sde_format *format,
		struct sde_rect *wb_roi);

/**
 * sde_encoder_helper_get_pp_line_count - pingpong linecount helper function
 * @drm_enc:    Pointer to drm encoder structure
 * @info:       structure used to populate the pp line count information
 */
void sde_encoder_helper_get_pp_line_count(struct drm_encoder *drm_enc,
		struct sde_hw_pp_vsync_info *info);

/**
 * sde_encoder_helper_get_kickoff_timeout_ms- get the kickoff timeout value based on fps
 * @drm_enc: Pointer to drm encoder structure
 * Returns: Kickoff timeout in milli seconds
 */
u32 sde_encoder_helper_get_kickoff_timeout_ms(struct drm_encoder *drm_enc);

/**
 * sde_encoder_helper_trigger_flush - control flush helper function
 *	This helper function may be optionally specified by physical
 *	encoders if they require ctl_flush triggering.
 * @phys_enc: Pointer to physical encoder structure
 */
void sde_encoder_helper_trigger_flush(struct sde_encoder_phys *phys_enc);

/**
 * sde_encoder_helper_trigger_start - control start helper function
 *	This helper function may be optionally specified by physical
 *	encoders if they require ctl_start triggering.
 * @phys_enc: Pointer to physical encoder structure
 */
void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc);

/**
 * sde_encoder_helper_vsync_config - configure vsync source for cmd mode
 * @phys_enc: Pointer to physical encoder structure
 * @vsync_source: vsync source selection
 */
void sde_encoder_helper_vsync_config(struct sde_encoder_phys *phys_enc, u32 vsync_source);

/**
 * sde_encoder_helper_wait_event_timeout - wait for event with timeout
 *	taking into account that jiffies may jump between reads leading to
 *	incorrectly detected timeouts. Prevent failure in this scenario by
 *	making sure that elapsed time during wait is valid.
 * @drm_id: drm object id for logging
 * @hw_id: hw instance id for logging
 * @info: wait info structure
 */
int sde_encoder_helper_wait_event_timeout(
		int32_t drm_id,
		int32_t hw_id,
		struct sde_encoder_wait_info *info);

/*
 * sde_encoder_get_fps - get the allowed panel jitter in nanoseconds
 * @encoder: Pointer to drm encoder object
 */
void sde_encoder_helper_get_jitter_bounds_ns(struct drm_encoder *encoder,
			u64 *l_bound, u64 *u_bound);

/**
 * sde_encoder_helper_switch_vsync - switch vsync source to WD or default
 * @drm_enc:     Pointer to drm encoder structure
 * @watchdog_te: switch vsync source to watchdog TE
 */
int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc,
		bool watchdog_te);

/**
 * sde_encoder_helper_hw_reset - issue ctl hw reset
 *	This helper function may be optionally specified by physical
 *	encoders if they require ctl hw reset. If state is currently
 *	SDE_ENC_ERR_NEEDS_HW_RESET, it is set back to SDE_ENC_ENABLED.
 * @phys_enc: Pointer to physical encoder structure
 */
void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc);

static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
		struct sde_encoder_phys *phys_enc)
{
	struct msm_display_topology def;
	enum sde_enc_split_role split_role;
	int ret, num_lm;
	bool mode_3d;

	if (!phys_enc || phys_enc->enable_state == SDE_ENC_DISABLING ||
			!phys_enc->connector || !phys_enc->connector->state)
		return BLEND_3D_NONE;

	ret = sde_connector_state_get_topology
			(phys_enc->connector->state, &def);
	if (ret)
		return BLEND_3D_NONE;

	if (phys_enc->hw_intf && phys_enc->hw_intf->cfg.split_link_en)
		return BLEND_3D_NONE;

	num_lm = def.num_lm;
	mode_3d = (num_lm > def.num_enc) ? true : false;
	split_role = phys_enc->split_role;

	if (split_role == ENC_ROLE_SOLO && num_lm == 2 && mode_3d)
		return BLEND_3D_H_ROW_INT;

	if ((split_role == ENC_ROLE_MASTER || split_role == ENC_ROLE_SLAVE)
			&& num_lm == 4 && mode_3d)
		return BLEND_3D_H_ROW_INT;

	return BLEND_3D_NONE;
}

/**
 * sde_encoder_phys_is_cwb_disabling - Check if CWB encoder attached to this
 *	 CRTC and it is in SDE_ENC_DISABLING state.
 * @phys_enc: Pointer to physical encoder structure
 * @crtc: drm crtc
 * @Return: true if cwb encoder is in disabling state
 */
static inline bool sde_encoder_phys_is_cwb_disabling(
	struct sde_encoder_phys *phys, struct drm_crtc *crtc)
{
	struct sde_encoder_phys_wb *wb_enc;

	if (!phys || !phys->in_clone_mode ||
				phys->enable_state != SDE_ENC_DISABLING)
		return false;

	wb_enc = container_of(phys, struct sde_encoder_phys_wb, base);
	return (wb_enc->crtc == crtc) ? true : false;
}

/**
 * sde_encoder_helper_split_config - split display configuration helper function
 *	This helper function may be used by physical encoders to configure
 *	the split display related registers.
 * @phys_enc: Pointer to physical encoder structure
 * @interface: enum sde_intf setting
 */
void sde_encoder_helper_split_config(
		struct sde_encoder_phys *phys_enc,
		enum sde_intf interface);

/**
 * sde_encoder_helper_reset_mixers - reset mixers associated with phys enc
 * @phys_enc: Pointer to physical encoder structure
 * @fb: Optional fb for specifying new mixer output resolution, may be NULL
 * Return: Zero on success
 */
int sde_encoder_helper_reset_mixers(struct sde_encoder_phys *phys_enc,
		struct drm_framebuffer *fb);

/**
 * sde_encoder_helper_report_irq_timeout - utility to report error that irq has
 *	timed out, including reporting frame error event to crtc and debug dump
 * @phys_enc: Pointer to physical encoder structure
 * @intr_idx: Failing interrupt index
 */
void sde_encoder_helper_report_irq_timeout(struct sde_encoder_phys *phys_enc,
		enum sde_intr_idx intr_idx);

/**
 * sde_encoder_helper_wait_for_irq - utility to wait on an irq.
 *	note: will call sde_encoder_helper_wait_for_irq on timeout
 * @phys_enc: Pointer to physical encoder structure
 * @intr_idx: encoder interrupt index
 * @wait_info: wait info struct
 * @Return: 0 or -ERROR
 */
int sde_encoder_helper_wait_for_irq(struct sde_encoder_phys *phys_enc,
		enum sde_intr_idx intr_idx,
		struct sde_encoder_wait_info *wait_info);

/**
 * sde_encoder_helper_register_irq - register and enable an irq
 * @phys_enc: Pointer to physical encoder structure
 * @intr_idx: encoder interrupt index
 * @Return: 0 or -ERROR
 */
int sde_encoder_helper_register_irq(struct sde_encoder_phys *phys_enc,
		enum sde_intr_idx intr_idx);

/**
 * sde_encoder_helper_unregister_irq - unregister and disable an irq
 * @phys_enc: Pointer to physical encoder structure
 * @intr_idx: encoder interrupt index
 * @Return: 0 or -ERROR
 */
int sde_encoder_helper_unregister_irq(struct sde_encoder_phys *phys_enc,
		enum sde_intr_idx intr_idx);

/**
 * sde_encoder_helper_update_intf_cfg - update interface configuration for
 *                                      single control path.
 * @phys_enc: Pointer to physical encoder structure
 */
void sde_encoder_helper_update_intf_cfg(
		struct sde_encoder_phys *phys_enc);

/**
 * _sde_encoder_phys_is_dual_ctl - check if encoder needs dual ctl path.
 * @phys_enc: Pointer to physical encoder structure
 * @Return: true if dual ctl paths else false
 */
static inline bool _sde_encoder_phys_is_dual_ctl(
		struct sde_encoder_phys *phys_enc)
{
	struct sde_kms *sde_kms;
	enum sde_rm_topology_name topology;
	const struct sde_rm_topology_def* def;

	if (!phys_enc) {
		pr_err("invalid phys_enc\n");
		return false;
	}

	sde_kms = phys_enc->sde_kms;
	if (!sde_kms) {
		pr_err("invalid kms\n");
		return false;
	}

	topology = sde_connector_get_topology_name(phys_enc->connector);
	def = sde_rm_topology_get_topology_def(&sde_kms->rm, topology);
	if (IS_ERR_OR_NULL(def)) {
		pr_err("invalid topology\n");
		return false;
	}

	return (def->num_ctl == 2) ? true : false;
}

/**
 * _sde_encoder_phys_is_ppsplit - check if pp_split is enabled
 * @phys_enc: Pointer to physical encoder structure
 * @Return: true or false
 */
static inline bool _sde_encoder_phys_is_ppsplit(
		struct sde_encoder_phys *phys_enc)
{
	enum sde_rm_topology_name topology;

	if (!phys_enc) {
		pr_err("invalid phys_enc\n");
		return false;
	}

	topology = sde_connector_get_topology_name(phys_enc->connector);
	if (topology == SDE_RM_TOPOLOGY_PPSPLIT)
		return true;

	return false;
}

static inline bool sde_encoder_phys_needs_single_flush(
		struct sde_encoder_phys *phys_enc)
{
	if (!phys_enc)
		return false;

	return (_sde_encoder_phys_is_ppsplit(phys_enc) ||
				!_sde_encoder_phys_is_dual_ctl(phys_enc));
}

/**
 * sde_encoder_helper_phys_disable - helper function to disable virt encoder
 * @phys_enc: Pointer to physical encoder structure
 * @wb_enc: Pointer to writeback encoder structure
 */
void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
		struct sde_encoder_phys_wb *wb_enc);

/**
 * sde_encoder_helper_phys_reset - helper function to reset virt encoder
 *                 if vsync is missing on phys encoder
 * @phys_enc: Pointer to physical encoder structure
 */
void sde_encoder_helper_phys_reset(struct sde_encoder_phys *phys_enc);

/**
 * sde_encoder_helper_setup_misr - helper function to setup misr
 * @phys_enc: Pointer to physical encoder structure
 * @enable: enable/disable flag
 * @frame_count: frame count for misr
 */
void sde_encoder_helper_setup_misr(struct sde_encoder_phys *phys_enc,
		bool enable, u32 frame_count);

/**
 * sde_encoder_helper_collect_misr - helper function to collect misr
 * @phys_enc: Pointer to physical encoder structure
 * @nonblock:  blocking/non-blocking flag
 * @misr_value:  pointer to misr value
 * @Return: zero on success
 */
int sde_encoder_helper_collect_misr(struct sde_encoder_phys *phys_enc,
		bool nonblock, u32 *misr_value);

#endif /* __sde_encoder_phys_H__ */
