From dc8e0291e4832da9a084558785b6a5d6286e0fee Mon Sep 17 00:00:00 2001 From: Abhijit Kulkarni Date: Tue, 11 Jun 2019 17:21:34 -0700 Subject: [PATCH] disp: msm: sde: move dsc implementation to a new file This change moves the DSC implementation into a separate file. This is required to add support for new compression algorithms. This cleanup change also, moves struct sde_encoder_virt declaration to the encoder header file. Change-Id: Idc3b96e65fcce2a7ee6e17af604cec0cb574f6f7 Signed-off-by: Abhijit Kulkarni --- msm/Makefile | 1 + msm/sde/sde_crtc.c | 4 +- msm/sde/sde_crtc.h | 12 +- msm/sde/sde_encoder.c | 767 +------------------------------------- msm/sde/sde_encoder.h | 170 ++++++++- msm/sde/sde_encoder_dce.c | 614 ++++++++++++++++++++++++++++++ msm/sde/sde_encoder_dce.h | 20 + msm/sde/sde_kms.h | 12 +- 8 files changed, 826 insertions(+), 774 deletions(-) create mode 100644 msm/sde/sde_encoder_dce.c create mode 100644 msm/sde/sde_encoder_dce.h diff --git a/msm/Makefile b/msm/Makefile index c8ccb270cc..2717193bd8 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -28,6 +28,7 @@ msm_drm-$(CONFIG_DRM_MSM_DP) += dp/dp_usbpd.o \ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_encoder.o \ + sde/sde_encoder_dce.o \ sde/sde_encoder_phys_vid.o \ sde/sde_encoder_phys_cmd.o \ sde/sde_irq.o \ diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 1c9a1dbc48..ce58186d96 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -2078,13 +2078,13 @@ static void sde_crtc_frame_event_cb(void *data, u32 event) struct sde_crtc *sde_crtc; struct msm_drm_private *priv; struct sde_crtc_frame_event *fevent; - struct sde_crtc_frame_event_cb_data *cb_data; + struct sde_kms_frame_event_cb_data *cb_data; struct drm_plane *plane; u32 ubwc_error; unsigned long flags; u32 crtc_id; - cb_data = (struct sde_crtc_frame_event_cb_data *)data; + cb_data = (struct sde_kms_frame_event_cb_data *)data; if (!data) { SDE_ERROR("invalid parameters\n"); return; diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index 21e70ed9c2..f22bf6a643 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -99,16 +99,6 @@ struct sde_crtc_mixer { u32 mixer_op_mode; }; -/** - * struct sde_crtc_frame_event_cb_data : info of drm objects of a frame event - * @crtc: pointer to drm crtc object registered for frame event - * @connector: pointer to drm connector which is source of frame event - */ -struct sde_crtc_frame_event_cb_data { - struct drm_crtc *crtc; - struct drm_connector *connector; -}; - /** * struct sde_crtc_frame_event: stores crtc frame event for crtc processing * @work: base work structure diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index f10540db02..dce49fb218 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -32,14 +32,15 @@ #include "sde_hw_intf.h" #include "sde_hw_ctl.h" #include "sde_formats.h" +#include "sde_encoder.h" #include "sde_encoder_phys.h" -#include "sde_power_handle.h" #include "sde_hw_dsc.h" #include "sde_crtc.h" #include "sde_trace.h" #include "sde_core_irq.h" #include "sde_hw_top.h" #include "sde_hw_qdss.h" +#include "sde_encoder_dce.h" #define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\ (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) @@ -59,15 +60,7 @@ (p) ? ((p)->hw_pp ? (p)->hw_pp->idx - PINGPONG_0 : -1) : -1, \ ##__VA_ARGS__) -/* - * Two to anticipate panels that can do cmd/vid dynamic switching - * plan is to create all possible physical encoder types, and switch between - * them at runtime - */ -#define NUM_PHYS_ENCODER_TYPES 2 -#define MAX_PHYS_ENCODERS_PER_VIRTUAL \ - (MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES) #define MISR_BUFF_SIZE 256 @@ -143,160 +136,6 @@ enum sde_enc_rc_events { SDE_ENC_RC_EVENT_EARLY_WAKEUP, }; -/* - * enum sde_enc_rc_states - states that the resource control maintains - * @SDE_ENC_RC_STATE_OFF: Resource is in OFF state - * @SDE_ENC_RC_STATE_PRE_OFF: Resource is transitioning to OFF state - * @SDE_ENC_RC_STATE_ON: Resource is in ON state - * @SDE_ENC_RC_STATE_MODESET: Resource is in modeset state - * @SDE_ENC_RC_STATE_IDLE: Resource is in IDLE state - */ -enum sde_enc_rc_states { - SDE_ENC_RC_STATE_OFF, - SDE_ENC_RC_STATE_PRE_OFF, - SDE_ENC_RC_STATE_ON, - SDE_ENC_RC_STATE_MODESET, - SDE_ENC_RC_STATE_IDLE -}; - -/** - * struct sde_encoder_virt - virtual encoder. Container of one or more physical - * encoders. Virtual encoder manages one "logical" display. Physical - * encoders manage one intf block, tied to a specific panel/sub-panel. - * Virtual encoder defers as much as possible to the physical encoders. - * Virtual encoder registers itself with the DRM Framework as the encoder. - * @base: drm_encoder base class for registration with DRM - * @enc_spin_lock: Virtual-Encoder-Wide Spin Lock for IRQ purposes - * @bus_scaling_client: Client handle to the bus scaling interface - * @te_source: vsync source pin information - * @ops: Encoder ops from init function - * @num_phys_encs: Actual number of physical encoders contained. - * @phys_encs: Container of physical encoders managed. - * @phys_vid_encs: Video physical encoders for panel mode switch. - * @phys_cmd_encs: Command physical encoders for panel mode switch. - * @cur_master: Pointer to the current master in this mode. Optimization - * Only valid after enable. Cleared as disable. - * @hw_pp Handle to the pingpong blocks used for the display. No. - * pingpong blocks can be different than num_phys_encs. - * @hw_dsc: Array of DSC block handles used for the display. - * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush - * @intfs_swapped Whether or not the phys_enc interfaces have been swapped - * for partial update right-only cases, such as pingpong - * split where virtual pingpong does not generate IRQs - @qdss_status: indicate if qdss is modified since last update - * @crtc_vblank_cb: Callback into the upper layer / CRTC for - * notification of the VBLANK - * @crtc_vblank_cb_data: Data from upper layer for VBLANK notification - * @crtc_kickoff_cb: Callback into CRTC that will flush & start - * all CTL paths - * @crtc_kickoff_cb_data: Opaque user data given to crtc_kickoff_cb - * @debugfs_root: Debug file system root file node - * @enc_lock: Lock around physical encoder create/destroy and - access. - * @frame_done_cnt: Atomic counter for tracking which phys_enc is - * done with frame processing. - * @crtc_frame_event_cb: callback handler for frame event - * @crtc_frame_event_cb_data: callback handler private data - * @vsync_event_timer: vsync timer - * @rsc_client: rsc client pointer - * @rsc_state_init: boolean to indicate rsc config init - * @disp_info: local copy of msm_display_info struct - * @misr_enable: misr enable/disable status - * @misr_frame_count: misr frame count before start capturing the data - * @idle_pc_enabled: indicate if idle power collapse is enabled - * currently. This can be controlled by user-mode - * @rc_lock: resource control mutex lock to protect - * virt encoder over various state changes - * @rc_state: resource controller state - * @delayed_off_work: delayed worker to schedule disabling of - * clks and resources after IDLE_TIMEOUT time. - * @vsync_event_work: worker to handle vsync event for autorefresh - * @input_event_work: worker to handle input device touch events - * @esd_trigger_work: worker to handle esd trigger events - * @input_handler: handler for input device events - * @topology: topology of the display - * @vblank_enabled: boolean to track userspace vblank vote - * @idle_pc_restore: flag to indicate idle_pc_restore happened - * @frame_trigger_mode: frame trigger mode indication for command - * mode display - * @dynamic_hdr_updated: flag to indicate if mempool was programmed - * @rsc_config: rsc configuration for display vtotal, fps, etc. - * @cur_conn_roi: current connector roi - * @prv_conn_roi: previous connector roi to optimize if unchanged - * @crtc pointer to drm_crtc - * @recovery_events_enabled: status of hw recovery feature enable by client - * @elevated_ahb_vote: increase AHB bus speed for the first frame - * after power collapse - * @mode_info: stores the current mode and should be used - * only in commit phase - */ -struct sde_encoder_virt { - struct drm_encoder base; - spinlock_t enc_spinlock; - struct mutex vblank_ctl_lock; - uint32_t bus_scaling_client; - - uint32_t display_num_of_h_tiles; - uint32_t te_source; - - struct sde_encoder_ops ops; - - unsigned int num_phys_encs; - struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; - struct sde_encoder_phys *phys_vid_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; - struct sde_encoder_phys *phys_cmd_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; - struct sde_encoder_phys *cur_master; - struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; - struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; - struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; - enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; - - bool intfs_swapped; - bool qdss_status; - - void (*crtc_vblank_cb)(void *data); - void *crtc_vblank_cb_data; - - struct dentry *debugfs_root; - struct mutex enc_lock; - atomic_t frame_done_cnt[MAX_PHYS_ENCODERS_PER_VIRTUAL]; - void (*crtc_frame_event_cb)(void *data, u32 event); - struct sde_crtc_frame_event_cb_data crtc_frame_event_cb_data; - - struct timer_list vsync_event_timer; - - struct sde_rsc_client *rsc_client; - bool rsc_state_init; - struct msm_display_info disp_info; - bool misr_enable; - u32 misr_frame_count; - - bool idle_pc_enabled; - struct mutex rc_lock; - enum sde_enc_rc_states rc_state; - struct kthread_delayed_work delayed_off_work; - struct kthread_work vsync_event_work; - struct kthread_work input_event_work; - struct kthread_work esd_trigger_work; - struct input_handler *input_handler; - struct msm_display_topology topology; - bool vblank_enabled; - bool idle_pc_restore; - enum frame_trigger_mode_type frame_trigger_mode; - bool dynamic_hdr_updated; - - struct sde_rsc_cmd_config rsc_config; - struct sde_rect cur_conn_roi; - struct sde_rect prv_conn_roi; - struct drm_crtc *crtc; - - bool recovery_events_enabled; - bool elevated_ahb_vote; - struct msm_mode_info mode_info; -}; - -#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base) - void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable) { struct sde_encoder_virt *sde_enc; @@ -384,30 +223,6 @@ static int _sde_encoder_wait_timeout(int32_t drm_id, int32_t hw_id, return rc; } -bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc) -{ - enum sde_rm_topology_name topology; - struct sde_encoder_virt *sde_enc; - struct drm_connector *drm_conn; - - if (!drm_enc) - return false; - - sde_enc = to_sde_encoder_virt(drm_enc); - if (!sde_enc->cur_master) - return false; - - drm_conn = sde_enc->cur_master->connector; - if (!drm_conn) - return false; - - topology = sde_connector_get_topology_name(drm_conn); - if (topology == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) - return true; - - return false; -} - bool sde_encoder_is_primary_display(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); @@ -1104,152 +919,6 @@ static int sde_encoder_virt_atomic_check( return ret; } -static int _sde_encoder_dsc_update_pic_dim(struct msm_display_dsc_info *dsc, - int pic_width, int pic_height) -{ - if (!dsc || !pic_width || !pic_height) { - SDE_ERROR("invalid input: pic_width=%d pic_height=%d\n", - pic_width, pic_height); - return -EINVAL; - } - - if ((pic_width % dsc->slice_width) || - (pic_height % dsc->slice_height)) { - SDE_ERROR("pic_dim=%dx%d has to be multiple of slice=%dx%d\n", - pic_width, pic_height, - dsc->slice_width, dsc->slice_height); - return -EINVAL; - } - - dsc->pic_width = pic_width; - dsc->pic_height = pic_height; - - return 0; -} - -static void _sde_encoder_dsc_pclk_param_calc(struct msm_display_dsc_info *dsc, - int intf_width) -{ - int slice_per_pkt, slice_per_intf; - int bytes_in_slice, total_bytes_per_intf; - - if (!dsc || !dsc->slice_width || !dsc->slice_per_pkt || - (intf_width < dsc->slice_width)) { - SDE_ERROR("invalid input: intf_width=%d slice_width=%d\n", - intf_width, dsc ? dsc->slice_width : -1); - return; - } - - slice_per_pkt = dsc->slice_per_pkt; - slice_per_intf = DIV_ROUND_UP(intf_width, dsc->slice_width); - - /* - * If slice_per_pkt is greater than slice_per_intf then default to 1. - * This can happen during partial update. - */ - if (slice_per_pkt > slice_per_intf) - slice_per_pkt = 1; - - bytes_in_slice = DIV_ROUND_UP(dsc->slice_width * dsc->bpp, 8); - total_bytes_per_intf = bytes_in_slice * slice_per_intf; - - dsc->eol_byte_num = total_bytes_per_intf % 3; - dsc->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3); - dsc->bytes_in_slice = bytes_in_slice; - dsc->bytes_per_pkt = bytes_in_slice * slice_per_pkt; - dsc->pkt_per_line = slice_per_intf / slice_per_pkt; -} - -static int _sde_encoder_dsc_initial_line_calc(struct msm_display_dsc_info *dsc, - int enc_ip_width) -{ - int max_ssm_delay, max_se_size, obuf_latency; - int input_ssm_out_latency, base_hs_latency; - int multi_hs_extra_latency, mux_word_size; - - /* Hardent core config */ - int max_muxword_size = 48; - int output_rate = 64; - int rtl_max_bpc = 10; - int pipeline_latency = 28; - - max_se_size = 4 * (rtl_max_bpc + 1); - max_ssm_delay = max_se_size + max_muxword_size - 1; - mux_word_size = (dsc->bpc >= 12 ? 64 : 48); - input_ssm_out_latency = pipeline_latency + (3 * (max_ssm_delay + 2)); - obuf_latency = DIV_ROUND_UP((9 * output_rate + - mux_word_size), dsc->bpp) + 1; - base_hs_latency = dsc->initial_xmit_delay + input_ssm_out_latency - + obuf_latency; - multi_hs_extra_latency = DIV_ROUND_UP((8 * dsc->chunk_size), dsc->bpp); - dsc->initial_lines = DIV_ROUND_UP((base_hs_latency + - multi_hs_extra_latency), dsc->slice_width); - - return 0; -} - -static bool _sde_encoder_dsc_ich_reset_override_needed(bool pu_en, - struct msm_display_dsc_info *dsc) -{ - /* - * As per the DSC spec, ICH_RESET can be either end of the slice line - * or at the end of the slice. HW internally generates ich_reset at - * end of the slice line if DSC_MERGE is used or encoder has two - * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE - * is not used then it will generate ich_reset at the end of slice. - * - * Now as per the spec, during one PPS session, position where - * ich_reset is generated should not change. Now if full-screen frame - * has more than 1 soft slice then HW will automatically generate - * ich_reset at the end of slice_line. But for the same panel, if - * partial frame is enabled and only 1 encoder is used with 1 slice, - * then HW will generate ich_reset at end of the slice. This is a - * mismatch. Prevent this by overriding HW's decision. - */ - return pu_en && dsc && (dsc->full_frame_slices > 1) && - (dsc->slice_width == dsc->pic_width); -} - -static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc, - struct sde_hw_pingpong *hw_pp, struct msm_display_dsc_info *dsc, - u32 common_mode, bool ich_reset, bool enable, - struct sde_hw_pingpong *hw_dsc_pp) -{ - if (!enable) { - if (hw_dsc_pp && hw_dsc_pp->ops.disable_dsc) - hw_dsc_pp->ops.disable_dsc(hw_dsc_pp); - - if (hw_dsc && hw_dsc->ops.dsc_disable) - hw_dsc->ops.dsc_disable(hw_dsc); - - if (hw_dsc && hw_dsc->ops.bind_pingpong_blk) - hw_dsc->ops.bind_pingpong_blk(hw_dsc, false, - PINGPONG_MAX); - return; - } - - if (!dsc || !hw_dsc || !hw_pp || !hw_dsc_pp) { - SDE_ERROR("invalid params %d %d %d %d\n", !dsc, !hw_dsc, - !hw_pp, !hw_dsc_pp); - return; - } - - if (hw_dsc->ops.dsc_config) - hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, ich_reset); - - if (hw_dsc->ops.dsc_config_thresh) - hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc); - - if (hw_dsc_pp->ops.setup_dsc) - hw_dsc_pp->ops.setup_dsc(hw_dsc_pp); - - if (hw_dsc->ops.bind_pingpong_blk) - hw_dsc->ops.bind_pingpong_blk(hw_dsc, true, hw_pp->idx); - - if (hw_dsc_pp->ops.enable_dsc) - hw_dsc_pp->ops.enable_dsc(hw_dsc_pp); -} - static void _sde_encoder_get_connector_roi( struct sde_encoder_virt *sde_enc, struct sde_rect *merged_conn_roi) @@ -1269,293 +938,6 @@ static void _sde_encoder_get_connector_roi( sde_kms_rect_merge_rectangles(&c_state->rois, merged_conn_roi); } -static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) -{ - int this_frame_slices; - int intf_ip_w, enc_ip_w; - int ich_res, dsc_common_mode = 0; - - struct sde_hw_pingpong *hw_pp = sde_enc->hw_pp[0]; - struct sde_hw_pingpong *hw_dsc_pp = sde_enc->hw_dsc_pp[0]; - struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0]; - struct sde_encoder_phys *enc_master = sde_enc->cur_master; - const struct sde_rect *roi = &sde_enc->cur_conn_roi; - struct msm_display_dsc_info *dsc = NULL; - struct sde_hw_ctl *hw_ctl; - struct sde_ctl_dsc_cfg cfg; - - if (hw_dsc == NULL || hw_pp == NULL || !enc_master) { - SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n"); - return -EINVAL; - } - - hw_ctl = enc_master->hw_ctl; - - memset(&cfg, 0, sizeof(cfg)); - dsc = &sde_enc->mode_info.comp_info.dsc_info; - _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h); - - this_frame_slices = roi->w / dsc->slice_width; - intf_ip_w = this_frame_slices * dsc->slice_width; - _sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w); - - enc_ip_w = intf_ip_w; - _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w); - - ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc); - - if (enc_master->intf_mode == INTF_MODE_VIDEO) - dsc_common_mode = DSC_MODE_VIDEO; - - SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", - roi->w, roi->h, dsc_common_mode); - SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, dsc_common_mode); - - _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, - ich_res, true, hw_dsc_pp); - cfg.dsc[cfg.dsc_count++] = hw_dsc->idx; - - /* setup dsc active configuration in the control path */ - if (hw_ctl->ops.setup_dsc_cfg) { - hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); - SDE_DEBUG_ENC(sde_enc, - "setup dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", - hw_ctl->idx, - cfg.dsc_count, - cfg.dsc[0], - cfg.dsc[1]); - } - - if (hw_ctl->ops.update_bitmask_dsc) - hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc->idx, 1); - - return 0; -} - -static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc, - struct sde_encoder_kickoff_params *params) -{ - int this_frame_slices; - int intf_ip_w, enc_ip_w; - int ich_res, dsc_common_mode; - - struct sde_encoder_phys *enc_master = sde_enc->cur_master; - const struct sde_rect *roi = &sde_enc->cur_conn_roi; - struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; - struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; - struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; - struct msm_display_dsc_info dsc[MAX_CHANNELS_PER_ENC]; - bool half_panel_partial_update; - struct sde_hw_ctl *hw_ctl = NULL; - struct sde_ctl_dsc_cfg cfg; - int i; - - if (!enc_master) { - SDE_ERROR_ENC(sde_enc, "invalid encoder master for DSC\n"); - return -EINVAL; - } - - memset(&cfg, 0, sizeof(cfg)); - - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - hw_pp[i] = sde_enc->hw_pp[i]; - hw_dsc[i] = sde_enc->hw_dsc[i]; - hw_dsc_pp[i] = sde_enc->hw_dsc_pp[i]; - - if (!hw_pp[i] || !hw_dsc[i] || !hw_dsc_pp[i]) { - SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n"); - return -EINVAL; - } - } - - hw_ctl = enc_master->hw_ctl; - - half_panel_partial_update = - hweight_long(params->affected_displays) == 1; - - dsc_common_mode = 0; - if (!half_panel_partial_update) - dsc_common_mode |= DSC_MODE_SPLIT_PANEL; - if (enc_master->intf_mode == INTF_MODE_VIDEO) - dsc_common_mode |= DSC_MODE_VIDEO; - - memcpy(&dsc[0], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[0])); - memcpy(&dsc[1], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[1])); - - /* - * Since both DSC use same pic dimension, set same pic dimension - * to both DSC structures. - */ - _sde_encoder_dsc_update_pic_dim(&dsc[0], roi->w, roi->h); - _sde_encoder_dsc_update_pic_dim(&dsc[1], roi->w, roi->h); - - this_frame_slices = roi->w / dsc[0].slice_width; - intf_ip_w = this_frame_slices * dsc[0].slice_width; - - if (!half_panel_partial_update) - intf_ip_w /= 2; - - /* - * In this topology when both interfaces are active, they have same - * load so intf_ip_w will be same. - */ - _sde_encoder_dsc_pclk_param_calc(&dsc[0], intf_ip_w); - _sde_encoder_dsc_pclk_param_calc(&dsc[1], intf_ip_w); - - /* - * In this topology, since there is no dsc_merge, uncompressed input - * to encoder and interface is same. - */ - enc_ip_w = intf_ip_w; - _sde_encoder_dsc_initial_line_calc(&dsc[0], enc_ip_w); - _sde_encoder_dsc_initial_line_calc(&dsc[1], enc_ip_w); - - /* - * __is_ich_reset_override_needed should be called only after - * updating pic dimension, mdss_panel_dsc_update_pic_dim. - */ - ich_res = _sde_encoder_dsc_ich_reset_override_needed( - half_panel_partial_update, &dsc[0]); - - SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", - roi->w, roi->h, dsc_common_mode); - - for (i = 0; i < sde_enc->num_phys_encs && - i < MAX_CHANNELS_PER_ENC; i++) { - bool active = !!((1 << i) & params->affected_displays); - - SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, - dsc_common_mode, i, active); - _sde_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], &dsc[i], - dsc_common_mode, ich_res, active, hw_dsc_pp[i]); - - if (active) { - if (cfg.dsc_count >= MAX_DSC_PER_CTL_V1) { - pr_err("Invalid dsc count:%d\n", - cfg.dsc_count); - return -EINVAL; - } - cfg.dsc[cfg.dsc_count++] = hw_dsc[i]->idx; - - if (hw_ctl->ops.update_bitmask_dsc) - hw_ctl->ops.update_bitmask_dsc(hw_ctl, - hw_dsc[i]->idx, 1); - } - } - - /* setup dsc active configuration in the control path */ - if (hw_ctl->ops.setup_dsc_cfg) { - hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); - SDE_DEBUG_ENC(sde_enc, - "setup dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", - hw_ctl->idx, - cfg.dsc_count, - cfg.dsc[0], - cfg.dsc[1]); - } - return 0; -} - -static int _sde_encoder_dsc_2_lm_2_enc_1_intf(struct sde_encoder_virt *sde_enc, - struct sde_encoder_kickoff_params *params) -{ - int this_frame_slices; - int intf_ip_w, enc_ip_w; - int ich_res, dsc_common_mode; - - struct sde_encoder_phys *enc_master = sde_enc->cur_master; - const struct sde_rect *roi = &sde_enc->cur_conn_roi; - struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; - struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; - struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; - struct msm_display_dsc_info *dsc = NULL; - bool half_panel_partial_update; - struct sde_hw_ctl *hw_ctl = NULL; - struct sde_ctl_dsc_cfg cfg; - int i; - - if (!enc_master) { - SDE_ERROR_ENC(sde_enc, "invalid encoder master for DSC\n"); - return -EINVAL; - } - - memset(&cfg, 0, sizeof(cfg)); - - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - hw_pp[i] = sde_enc->hw_pp[i]; - hw_dsc[i] = sde_enc->hw_dsc[i]; - hw_dsc_pp[i] = sde_enc->hw_dsc_pp[i]; - - if (!hw_pp[i] || !hw_dsc[i] || !hw_dsc_pp[i]) { - SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n"); - return -EINVAL; - } - } - - hw_ctl = enc_master->hw_ctl; - - dsc = &sde_enc->mode_info.comp_info.dsc_info; - - half_panel_partial_update = - hweight_long(params->affected_displays) == 1; - - dsc_common_mode = 0; - if (!half_panel_partial_update) - dsc_common_mode |= DSC_MODE_SPLIT_PANEL | DSC_MODE_MULTIPLEX; - if (enc_master->intf_mode == INTF_MODE_VIDEO) - dsc_common_mode |= DSC_MODE_VIDEO; - - _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h); - - this_frame_slices = roi->w / dsc->slice_width; - intf_ip_w = this_frame_slices * dsc->slice_width; - _sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w); - - /* - * dsc merge case: when using 2 encoders for the same stream, - * no. of slices need to be same on both the encoders. - */ - enc_ip_w = intf_ip_w / 2; - _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w); - - ich_res = _sde_encoder_dsc_ich_reset_override_needed( - half_panel_partial_update, dsc); - - SDE_DEBUG_ENC(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", - roi->w, roi->h, dsc_common_mode); - SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, - dsc_common_mode, i, params->affected_displays); - - _sde_encoder_dsc_pipe_cfg(hw_dsc[0], hw_pp[0], dsc, dsc_common_mode, - ich_res, true, hw_dsc_pp[0]); - cfg.dsc[0] = hw_dsc[0]->idx; - cfg.dsc_count++; - if (hw_ctl->ops.update_bitmask_dsc) - hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc[0]->idx, 1); - - - _sde_encoder_dsc_pipe_cfg(hw_dsc[1], hw_pp[1], dsc, dsc_common_mode, - ich_res, !half_panel_partial_update, hw_dsc_pp[1]); - if (!half_panel_partial_update) { - cfg.dsc[1] = hw_dsc[1]->idx; - cfg.dsc_count++; - if (hw_ctl->ops.update_bitmask_dsc) - hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc[1]->idx, - 1); - } - /* setup dsc active configuration in the control path */ - if (hw_ctl->ops.setup_dsc_cfg) { - hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); - SDE_DEBUG_ENC(sde_enc, - "setup_dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", - hw_ctl->idx, - cfg.dsc_count, - cfg.dsc[0], - cfg.dsc[1]); - } - return 0; -} - static int _sde_encoder_update_roi(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc; @@ -1595,62 +977,6 @@ static int _sde_encoder_update_roi(struct drm_encoder *drm_enc) return 0; } -static int _sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc, - struct sde_encoder_kickoff_params *params) -{ - enum sde_rm_topology_name topology; - struct drm_connector *drm_conn; - int ret = 0; - - if (!sde_enc || !params || !sde_enc->phys_encs[0] || - !sde_enc->phys_encs[0]->connector) - return -EINVAL; - - drm_conn = sde_enc->phys_encs[0]->connector; - - topology = sde_connector_get_topology_name(drm_conn); - if (topology == SDE_RM_TOPOLOGY_NONE) { - SDE_ERROR_ENC(sde_enc, "topology not set yet\n"); - return -EINVAL; - } - - SDE_DEBUG_ENC(sde_enc, "topology:%d\n", topology); - SDE_EVT32(DRMID(&sde_enc->base), topology, - sde_enc->cur_conn_roi.x, - sde_enc->cur_conn_roi.y, - sde_enc->cur_conn_roi.w, - sde_enc->cur_conn_roi.h, - sde_enc->prv_conn_roi.x, - sde_enc->prv_conn_roi.y, - sde_enc->prv_conn_roi.w, - sde_enc->prv_conn_roi.h, - sde_enc->cur_master->cached_mode.hdisplay, - sde_enc->cur_master->cached_mode.vdisplay); - - if (sde_kms_rect_is_equal(&sde_enc->cur_conn_roi, - &sde_enc->prv_conn_roi)) - return ret; - - switch (topology) { - case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC: - case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC: - ret = _sde_encoder_dsc_n_lm_1_enc_1_intf(sde_enc); - break; - case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE: - ret = _sde_encoder_dsc_2_lm_2_enc_1_intf(sde_enc, params); - break; - case SDE_RM_TOPOLOGY_DUALPIPE_DSC: - ret = _sde_encoder_dsc_2_lm_2_enc_2_intf(sde_enc, params); - break; - default: - SDE_ERROR_ENC(sde_enc, "No DSC support for topology %d", - topology); - return -EINVAL; - } - - return ret; -} - void sde_encoder_helper_vsync_config(struct sde_encoder_phys *phys_enc, u32 vsync_source, bool is_dummy) { @@ -1743,51 +1069,7 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc, } } -static void _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) -{ - int i; - struct sde_hw_pingpong *hw_pp = NULL; - struct sde_hw_pingpong *hw_dsc_pp = NULL; - struct sde_hw_dsc *hw_dsc = NULL; - struct sde_hw_ctl *hw_ctl = NULL; - struct sde_ctl_dsc_cfg cfg; - if (!sde_enc || !sde_enc->phys_encs[0] || - !sde_enc->phys_encs[0]->connector) { - SDE_ERROR("invalid params %d %d\n", - !sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1); - return; - } - - if (sde_enc->cur_master) - hw_ctl = sde_enc->cur_master->hw_ctl; - - /* Disable DSC for all the pp's present in this topology */ - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - hw_pp = sde_enc->hw_pp[i]; - hw_dsc = sde_enc->hw_dsc[i]; - hw_dsc_pp = sde_enc->hw_dsc_pp[i]; - - _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, NULL, - 0, 0, 0, hw_dsc_pp); - - if (hw_dsc) - sde_enc->dirty_dsc_ids[i] = hw_dsc->idx; - } - - /* Clear the DSC ACTIVE config for this CTL */ - if (hw_ctl && hw_ctl->ops.setup_dsc_cfg) { - memset(&cfg, 0, sizeof(cfg)); - hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); - } - - /** - * Since pending flushes from previous commit get cleared - * sometime after this point, setting DSC flush bits now - * will have no effect. Therefore dirty_dsc_ids track which - * DSC blocks must be flushed for the next trigger. - */ -} int sde_encoder_helper_switch_vsync(struct drm_encoder *drm_enc, bool watchdog_te) @@ -2843,7 +2125,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc, * Disable dsc before switch the mode and after pre_modeset, * to guarantee that previous kickoff finished. */ - _sde_encoder_dsc_disable(sde_enc); + sde_encoder_dsc_disable(sde_enc); } else if (msm_is_mode_seamless_poms(adj_mode)) { _sde_encoder_modeset_helper_locked(drm_enc, SDE_ENC_RC_EVENT_PRE_MODESET); @@ -3359,7 +2641,7 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) * and after physical encoder is disabled, to make sure timing * engine is already disabled (for video mode). */ - _sde_encoder_dsc_disable(sde_enc); + sde_encoder_dsc_disable(sde_enc); sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_STOP); @@ -4577,39 +3859,6 @@ static int _helper_flush_qsync(struct sde_encoder_phys *phys_enc) return 0; } -static bool _sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc) -{ - int i; - - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - /** - * This dirty_dsc_hw field is set during DSC disable to - * indicate which DSC blocks need to be flushed - */ - if (sde_enc->dirty_dsc_ids[i]) - return true; - } - - return false; -} - -static void _helper_flush_dsc(struct sde_encoder_virt *sde_enc) -{ - int i; - struct sde_hw_ctl *hw_ctl = NULL; - enum sde_dsc dsc_idx; - - if (sde_enc->cur_master) - hw_ctl = sde_enc->cur_master->hw_ctl; - - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { - dsc_idx = sde_enc->dirty_dsc_ids[i]; - if (dsc_idx && hw_ctl && hw_ctl->ops.update_bitmask_dsc) - hw_ctl->ops.update_bitmask_dsc(hw_ctl, dsc_idx, 1); - - sde_enc->dirty_dsc_ids[i] = DSC_NONE; - } -} static void _sde_encoder_helper_hdr_plus_mempool_update( struct sde_encoder_virt *sde_enc) { @@ -4739,15 +3988,15 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc, if (_sde_encoder_is_dsc_enabled(drm_enc) && sde_enc->cur_master && ((is_cmd_mode && sde_enc->cur_master->cont_splash_enabled) || !sde_enc->cur_master->cont_splash_enabled)) { - rc = _sde_encoder_dsc_setup(sde_enc, params); + rc = sde_encoder_dsc_setup(sde_enc, params); if (rc) { SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc); ret = rc; } } - if (_sde_encoder_dsc_is_dirty(sde_enc)) - _helper_flush_dsc(sde_enc); + if (sde_encoder_dsc_is_dirty(sde_enc)) + sde_encoder_dsc_helper_flush_dsc(sde_enc); if (sde_enc->cur_master && !sde_enc->cur_master->cont_splash_enabled) sde_configure_qdss(sde_enc, sde_enc->cur_master->hw_qdss, diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index b4ca285601..e01a9f9acb 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -20,11 +20,23 @@ #define __SDE_ENCODER_H__ #include +#include #include "msm_prop.h" #include "sde_hw_mdss.h" #include "sde_kms.h" #include "sde_connector.h" +#include "sde_power_handle.h" + +/* + * Two to anticipate panels that can do cmd/vid dynamic switching + * plan is to create all possible physical encoder types, and switch between + * them at runtime + */ +#define NUM_PHYS_ENCODER_TYPES 2 + +#define MAX_PHYS_ENCODERS_PER_VIRTUAL \ + (MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES) #define MAX_CHANNELS_PER_ENC 2 @@ -69,6 +81,22 @@ struct sde_encoder_kickoff_params { enum frame_trigger_mode_type frame_trigger_mode; }; +/* + * enum sde_enc_rc_states - states that the resource control maintains + * @SDE_ENC_RC_STATE_OFF: Resource is in OFF state + * @SDE_ENC_RC_STATE_PRE_OFF: Resource is transitioning to OFF state + * @SDE_ENC_RC_STATE_ON: Resource is in ON state + * @SDE_ENC_RC_STATE_MODESET: Resource is in modeset state + * @SDE_ENC_RC_STATE_IDLE: Resource is in IDLE state + */ +enum sde_enc_rc_states { + SDE_ENC_RC_STATE_OFF, + SDE_ENC_RC_STATE_PRE_OFF, + SDE_ENC_RC_STATE_ON, + SDE_ENC_RC_STATE_MODESET, + SDE_ENC_RC_STATE_IDLE +}; + /** * struct sde_encoder_ops - callback functions for generic sde encoder * Individual callbacks documented below. @@ -85,6 +113,146 @@ struct sde_encoder_ops { u32 controller_id, void *phys_init_params); }; +/** + * struct sde_encoder_virt - virtual encoder. Container of one or more physical + * encoders. Virtual encoder manages one "logical" display. Physical + * encoders manage one intf block, tied to a specific panel/sub-panel. + * Virtual encoder defers as much as possible to the physical encoders. + * Virtual encoder registers itself with the DRM Framework as the encoder. + * @base: drm_encoder base class for registration with DRM + * @enc_spin_lock: Virtual-Encoder-Wide Spin Lock for IRQ purposes + * @bus_scaling_client: Client handle to the bus scaling interface + * @te_source: vsync source pin information + * @ops: Encoder ops from init function + * @num_phys_encs: Actual number of physical encoders contained. + * @phys_encs: Container of physical encoders managed. + * @phys_vid_encs: Video physical encoders for panel mode switch. + * @phys_cmd_encs: Command physical encoders for panel mode switch. + * @cur_master: Pointer to the current master in this mode. Optimization + * Only valid after enable. Cleared as disable. + * @hw_pp Handle to the pingpong blocks used for the display. No. + * pingpong blocks can be different than num_phys_encs. + * @hw_dsc: Array of DSC block handles used for the display. + * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush + * @intfs_swapped Whether or not the phys_enc interfaces have been swapped + * for partial update right-only cases, such as pingpong + * split where virtual pingpong does not generate IRQs + * @qdss_status: indicate if qdss is modified since last update + * @crtc_vblank_cb: Callback into the upper layer / CRTC for + * notification of the VBLANK + * @crtc_vblank_cb_data: Data from upper layer for VBLANK notification + * @crtc_kickoff_cb: Callback into CRTC that will flush & start + * all CTL paths + * @crtc_kickoff_cb_data: Opaque user data given to crtc_kickoff_cb + * @debugfs_root: Debug file system root file node + * @enc_lock: Lock around physical encoder create/destroy and + access. + * @frame_done_cnt: Atomic counter for tracking which phys_enc is + * done with frame processing + * @crtc_frame_event_cb: callback handler for frame event + * @crtc_frame_event_cb_data: callback handler private data + * @vsync_event_timer: vsync timer + * @rsc_client: rsc client pointer + * @rsc_state_init: boolean to indicate rsc config init + * @disp_info: local copy of msm_display_info struct + * @misr_enable: misr enable/disable status + * @misr_frame_count: misr frame count before start capturing the data + * @idle_pc_enabled: indicate if idle power collapse is enabled + * currently. This can be controlled by user-mode + * @rc_lock: resource control mutex lock to protect + * virt encoder over various state changes + * @rc_state: resource controller state + * @delayed_off_work: delayed worker to schedule disabling of + * clks and resources after IDLE_TIMEOUT time. + * @vsync_event_work: worker to handle vsync event for autorefresh + * @input_event_work: worker to handle input device touch events + * @esd_trigger_work: worker to handle esd trigger events + * @input_handler: handler for input device events + * @topology: topology of the display + * @vblank_enabled: boolean to track userspace vblank vote + * @idle_pc_restore: flag to indicate idle_pc_restore happened + * @frame_trigger_mode: frame trigger mode indication for command mode + * display + * @dynamic_hdr_updated: flag to indicate if mempool was unchanged + * @rsc_config: rsc configuration for display vtotal, fps, etc. + * @cur_conn_roi: current connector roi + * @prv_conn_roi: previous connector roi to optimize if unchanged + * @crtc pointer to drm_crtc + * @recovery_events_enabled: status of hw recovery feature enable by client + * @elevated_ahb_vote: increase AHB bus speed for the first frame + * after power collapse + * @pm_qos_cpu_req: pm_qos request for cpu frequency + * @mode_info: stores the current mode and should be used + * only in commit phase + */ +struct sde_encoder_virt { + struct drm_encoder base; + spinlock_t enc_spinlock; + struct mutex vblank_ctl_lock; + uint32_t bus_scaling_client; + + uint32_t display_num_of_h_tiles; + uint32_t te_source; + + struct sde_encoder_ops ops; + + unsigned int num_phys_encs; + struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *phys_vid_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *phys_cmd_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + struct sde_encoder_phys *cur_master; + struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; + struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; + enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; + + bool intfs_swapped; + bool qdss_status; + + void (*crtc_vblank_cb)(void *data); + void *crtc_vblank_cb_data; + + struct dentry *debugfs_root; + struct mutex enc_lock; + atomic_t frame_done_cnt[MAX_PHYS_ENCODERS_PER_VIRTUAL]; + void (*crtc_frame_event_cb)(void *data, u32 event); + struct sde_kms_frame_event_cb_data crtc_frame_event_cb_data; + + struct timer_list vsync_event_timer; + + struct sde_rsc_client *rsc_client; + bool rsc_state_init; + struct msm_display_info disp_info; + bool misr_enable; + u32 misr_frame_count; + + bool idle_pc_enabled; + struct mutex rc_lock; + enum sde_enc_rc_states rc_state; + struct kthread_delayed_work delayed_off_work; + struct kthread_work vsync_event_work; + struct kthread_work input_event_work; + struct kthread_work esd_trigger_work; + struct input_handler *input_handler; + struct msm_display_topology topology; + bool vblank_enabled; + bool idle_pc_restore; + enum frame_trigger_mode_type frame_trigger_mode; + bool dynamic_hdr_updated; + + struct sde_rsc_cmd_config rsc_config; + struct sde_rect cur_conn_roi; + struct sde_rect prv_conn_roi; + struct drm_crtc *crtc; + + bool recovery_events_enabled; + bool elevated_ahb_vote; + struct pm_qos_request pm_qos_cpu_req; + struct msm_mode_info mode_info; +}; + +#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base) + /** * sde_encoder_get_hw_resources - Populate table of required hardware resources * @encoder: encoder pointer diff --git a/msm/sde/sde_encoder_dce.c b/msm/sde/sde_encoder_dce.c new file mode 100644 index 0000000000..71b2e087a4 --- /dev/null +++ b/msm/sde/sde_encoder_dce.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include + +#include "msm_drv.h" +#include "sde_kms.h" +#include +#include +#include "sde_hwio.h" +#include "sde_hw_catalog.h" +#include "sde_hw_intf.h" +#include "sde_hw_ctl.h" +#include "sde_formats.h" +#include "sde_encoder_phys.h" +#include "sde_power_handle.h" +#include "sde_hw_dsc.h" +#include "sde_crtc.h" +#include "sde_trace.h" +#include "sde_core_irq.h" + +#define SDE_DEBUG_DCE(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\ + (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) + +#define SDE_ERROR_DCE(e, fmt, ...) SDE_ERROR("enc%d " fmt,\ + (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) + +bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc) +{ + enum sde_rm_topology_name topology; + struct sde_encoder_virt *sde_enc; + struct drm_connector *drm_conn; + + if (!drm_enc) + return false; + + sde_enc = to_sde_encoder_virt(drm_enc); + if (!sde_enc->cur_master) + return false; + + drm_conn = sde_enc->cur_master->connector; + if (!drm_conn) + return false; + + topology = sde_connector_get_topology_name(drm_conn); + if (topology == SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE) + return true; + + return false; +} + +static int _sde_encoder_dsc_update_pic_dim(struct msm_display_dsc_info *dsc, + + int pic_width, int pic_height) +{ + if (!dsc || !pic_width || !pic_height) { + SDE_ERROR("invalid input: pic_width=%d pic_height=%d\n", + pic_width, pic_height); + return -EINVAL; + } + + if ((pic_width % dsc->slice_width) || + (pic_height % dsc->slice_height)) { + SDE_ERROR("pic_dim=%dx%d has to be multiple of slice=%dx%d\n", + pic_width, pic_height, + dsc->slice_width, dsc->slice_height); + return -EINVAL; + } + + dsc->pic_width = pic_width; + dsc->pic_height = pic_height; + + return 0; +} + +static void _sde_encoder_dsc_pclk_param_calc(struct msm_display_dsc_info *dsc, + int intf_width) +{ + int slice_per_pkt, slice_per_intf; + int bytes_in_slice, total_bytes_per_intf; + + if (!dsc || !dsc->slice_width || !dsc->slice_per_pkt || + (intf_width < dsc->slice_width)) { + SDE_ERROR("invalid input: intf_width=%d slice_width=%d\n", + intf_width, dsc ? dsc->slice_width : -1); + return; + } + + slice_per_pkt = dsc->slice_per_pkt; + slice_per_intf = DIV_ROUND_UP(intf_width, dsc->slice_width); + + /* + * If slice_per_pkt is greater than slice_per_intf then default to 1. + * This can happen during partial update. + */ + if (slice_per_pkt > slice_per_intf) + slice_per_pkt = 1; + + bytes_in_slice = DIV_ROUND_UP(dsc->slice_width * dsc->bpp, 8); + total_bytes_per_intf = bytes_in_slice * slice_per_intf; + + dsc->eol_byte_num = total_bytes_per_intf % 3; + dsc->pclk_per_line = DIV_ROUND_UP(total_bytes_per_intf, 3); + dsc->bytes_in_slice = bytes_in_slice; + dsc->bytes_per_pkt = bytes_in_slice * slice_per_pkt; + dsc->pkt_per_line = slice_per_intf / slice_per_pkt; +} + +static int _sde_encoder_dsc_initial_line_calc(struct msm_display_dsc_info *dsc, + int enc_ip_width) +{ + int max_ssm_delay, max_se_size, obuf_latency; + int input_ssm_out_latency, base_hs_latency; + int multi_hs_extra_latency, mux_word_size; + + /* Hardent core config */ + int max_muxword_size = 48; + int output_rate = 64; + int rtl_max_bpc = 10; + int pipeline_latency = 28; + + max_se_size = 4 * (rtl_max_bpc + 1); + max_ssm_delay = max_se_size + max_muxword_size - 1; + mux_word_size = (dsc->bpc >= 12 ? 64 : 48); + input_ssm_out_latency = pipeline_latency + (3 * (max_ssm_delay + 2)); + obuf_latency = DIV_ROUND_UP((9 * output_rate + + mux_word_size), dsc->bpp) + 1; + base_hs_latency = dsc->initial_xmit_delay + input_ssm_out_latency + + obuf_latency; + multi_hs_extra_latency = DIV_ROUND_UP((8 * dsc->chunk_size), dsc->bpp); + dsc->initial_lines = DIV_ROUND_UP((base_hs_latency + + multi_hs_extra_latency), dsc->slice_width); + + return 0; +} + +static bool _sde_encoder_dsc_ich_reset_override_needed(bool pu_en, + struct msm_display_dsc_info *dsc) +{ + /* + * As per the DSC spec, ICH_RESET can be either end of the slice line + * or at the end of the slice. HW internally generates ich_reset at + * end of the slice line if DSC_MERGE is used or encoder has two + * soft slices. However, if encoder has only 1 soft slice and DSC_MERGE + * is not used then it will generate ich_reset at the end of slice. + * + * Now as per the spec, during one PPS session, position where + * ich_reset is generated should not change. Now if full-screen frame + * has more than 1 soft slice then HW will automatically generate + * ich_reset at the end of slice_line. But for the same panel, if + * partial frame is enabled and only 1 encoder is used with 1 slice, + * then HW will generate ich_reset at end of the slice. This is a + * mismatch. Prevent this by overriding HW's decision. + */ + return pu_en && dsc && (dsc->full_frame_slices > 1) && + (dsc->slice_width == dsc->pic_width); +} + +static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc, + struct sde_hw_pingpong *hw_pp, struct msm_display_dsc_info *dsc, + u32 common_mode, bool ich_reset, bool enable, + struct sde_hw_pingpong *hw_dsc_pp) +{ + if (!enable) { + if (hw_dsc_pp && hw_dsc_pp->ops.disable_dsc) + hw_dsc_pp->ops.disable_dsc(hw_dsc_pp); + + if (hw_dsc && hw_dsc->ops.dsc_disable) + hw_dsc->ops.dsc_disable(hw_dsc); + + if (hw_dsc && hw_dsc->ops.bind_pingpong_blk) + hw_dsc->ops.bind_pingpong_blk(hw_dsc, false, + PINGPONG_MAX); + return; + } + + if (!dsc || !hw_dsc || !hw_pp || !hw_dsc_pp) { + SDE_ERROR("invalid params %d %d %d %d\n", !dsc, !hw_dsc, + !hw_pp, !hw_dsc_pp); + return; + } + + if (hw_dsc->ops.dsc_config) + hw_dsc->ops.dsc_config(hw_dsc, dsc, common_mode, ich_reset); + + if (hw_dsc->ops.dsc_config_thresh) + hw_dsc->ops.dsc_config_thresh(hw_dsc, dsc); + + if (hw_dsc_pp->ops.setup_dsc) + hw_dsc_pp->ops.setup_dsc(hw_dsc_pp); + + if (hw_dsc->ops.bind_pingpong_blk) + hw_dsc->ops.bind_pingpong_blk(hw_dsc, true, hw_pp->idx); + + if (hw_dsc_pp->ops.enable_dsc) + hw_dsc_pp->ops.enable_dsc(hw_dsc_pp); +} + +static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc) +{ + int this_frame_slices; + int intf_ip_w, enc_ip_w; + int ich_res, dsc_common_mode = 0; + + struct sde_hw_pingpong *hw_pp = sde_enc->hw_pp[0]; + struct sde_hw_pingpong *hw_dsc_pp = sde_enc->hw_dsc_pp[0]; + struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0]; + struct sde_encoder_phys *enc_master = sde_enc->cur_master; + const struct sde_rect *roi = &sde_enc->cur_conn_roi; + struct msm_display_dsc_info *dsc = NULL; + struct sde_hw_ctl *hw_ctl; + struct sde_ctl_dsc_cfg cfg; + + if (hw_dsc == NULL || hw_pp == NULL || !enc_master) { + SDE_ERROR_DCE(sde_enc, "invalid params for DSC\n"); + return -EINVAL; + } + + + hw_ctl = enc_master->hw_ctl; + + memset(&cfg, 0, sizeof(cfg)); + dsc = &sde_enc->mode_info.comp_info.dsc_info; + _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h); + + this_frame_slices = roi->w / dsc->slice_width; + intf_ip_w = this_frame_slices * dsc->slice_width; + _sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w); + + enc_ip_w = intf_ip_w; + _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w); + + ich_res = _sde_encoder_dsc_ich_reset_override_needed(false, dsc); + + if (enc_master->intf_mode == INTF_MODE_VIDEO) + dsc_common_mode = DSC_MODE_VIDEO; + + SDE_DEBUG_DCE(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", + roi->w, roi->h, dsc_common_mode); + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, dsc_common_mode); + + _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode, + ich_res, true, hw_dsc_pp); + cfg.dsc[cfg.dsc_count++] = hw_dsc->idx; + + /* setup dsc active configuration in the control path */ + if (hw_ctl->ops.setup_dsc_cfg) { + hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); + SDE_DEBUG_DCE(sde_enc, + "setup dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", + hw_ctl->idx, + cfg.dsc_count, + cfg.dsc[0], + cfg.dsc[1]); + } + + if (hw_ctl->ops.update_bitmask_dsc) + hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc->idx, 1); + + return 0; +} + +static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc, + struct sde_encoder_kickoff_params *params) +{ + int this_frame_slices; + int intf_ip_w, enc_ip_w; + int ich_res, dsc_common_mode; + + struct sde_encoder_phys *enc_master = sde_enc->cur_master; + const struct sde_rect *roi = &sde_enc->cur_conn_roi; + struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; + struct msm_display_dsc_info dsc[MAX_CHANNELS_PER_ENC]; + bool half_panel_partial_update; + struct sde_hw_ctl *hw_ctl = enc_master->hw_ctl; + struct sde_ctl_dsc_cfg cfg; + int i; + + memset(&cfg, 0, sizeof(cfg)); + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_pp[i] = sde_enc->hw_pp[i]; + hw_dsc[i] = sde_enc->hw_dsc[i]; + hw_dsc_pp[i] = sde_enc->hw_dsc_pp[i]; + + if (!hw_pp[i] || !hw_dsc[i] || !hw_dsc_pp[i]) { + SDE_ERROR_DCE(sde_enc, "invalid params for DSC\n"); + return -EINVAL; + } + } + + half_panel_partial_update = + hweight_long(params->affected_displays) == 1; + + dsc_common_mode = 0; + if (!half_panel_partial_update) + dsc_common_mode |= DSC_MODE_SPLIT_PANEL; + if (enc_master->intf_mode == INTF_MODE_VIDEO) + dsc_common_mode |= DSC_MODE_VIDEO; + + memcpy(&dsc[0], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[0])); + memcpy(&dsc[1], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[1])); + + /* + * Since both DSC use same pic dimension, set same pic dimension + * to both DSC structures. + */ + _sde_encoder_dsc_update_pic_dim(&dsc[0], roi->w, roi->h); + _sde_encoder_dsc_update_pic_dim(&dsc[1], roi->w, roi->h); + + this_frame_slices = roi->w / dsc[0].slice_width; + intf_ip_w = this_frame_slices * dsc[0].slice_width; + + if (!half_panel_partial_update) + intf_ip_w /= 2; + + /* + * In this topology when both interfaces are active, they have same + * load so intf_ip_w will be same. + */ + _sde_encoder_dsc_pclk_param_calc(&dsc[0], intf_ip_w); + _sde_encoder_dsc_pclk_param_calc(&dsc[1], intf_ip_w); + + /* + * In this topology, since there is no dsc_merge, uncompressed input + * to encoder and interface is same. + */ + enc_ip_w = intf_ip_w; + _sde_encoder_dsc_initial_line_calc(&dsc[0], enc_ip_w); + _sde_encoder_dsc_initial_line_calc(&dsc[1], enc_ip_w); + + /* + * __is_ich_reset_override_needed should be called only after + * updating pic dimension, mdss_panel_dsc_update_pic_dim. + */ + ich_res = _sde_encoder_dsc_ich_reset_override_needed( + half_panel_partial_update, &dsc[0]); + + SDE_DEBUG_DCE(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", + roi->w, roi->h, dsc_common_mode); + + for (i = 0; i < sde_enc->num_phys_encs; i++) { + bool active = !!((1 << i) & params->affected_displays); + + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, + dsc_common_mode, i, active); + _sde_encoder_dsc_pipe_cfg(hw_dsc[i], hw_pp[i], &dsc[i], + dsc_common_mode, ich_res, active, hw_dsc_pp[i]); + + if (active) { + if (cfg.dsc_count >= MAX_DSC_PER_CTL_V1) { + pr_err("Invalid dsc count:%d\n", + cfg.dsc_count); + return -EINVAL; + } + cfg.dsc[cfg.dsc_count++] = hw_dsc[i]->idx; + + if (hw_ctl->ops.update_bitmask_dsc) + hw_ctl->ops.update_bitmask_dsc(hw_ctl, + hw_dsc[i]->idx, 1); + } + } + + /* setup dsc active configuration in the control path */ + if (hw_ctl->ops.setup_dsc_cfg) { + hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); + SDE_DEBUG_DCE(sde_enc, + "setup dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", + hw_ctl->idx, + cfg.dsc_count, + cfg.dsc[0], + cfg.dsc[1]); + } + return 0; +} + +static int _sde_encoder_dsc_2_lm_2_enc_1_intf(struct sde_encoder_virt *sde_enc, + struct sde_encoder_kickoff_params *params) +{ + int this_frame_slices; + int intf_ip_w, enc_ip_w; + int ich_res, dsc_common_mode; + + struct sde_encoder_phys *enc_master = sde_enc->cur_master; + const struct sde_rect *roi = &sde_enc->cur_conn_roi; + struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; + struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; + struct msm_display_dsc_info *dsc = NULL; + bool half_panel_partial_update; + struct sde_hw_ctl *hw_ctl = enc_master->hw_ctl; + struct sde_ctl_dsc_cfg cfg; + int i; + + memset(&cfg, 0, sizeof(cfg)); + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_pp[i] = sde_enc->hw_pp[i]; + hw_dsc[i] = sde_enc->hw_dsc[i]; + hw_dsc_pp[i] = sde_enc->hw_dsc_pp[i]; + + if (!hw_pp[i] || !hw_dsc[i] || !hw_dsc_pp[i]) { + SDE_ERROR_DCE(sde_enc, "invalid params for DSC\n"); + return -EINVAL; + } + } + + dsc = &sde_enc->mode_info.comp_info.dsc_info; + + half_panel_partial_update = + hweight_long(params->affected_displays) == 1; + + dsc_common_mode = 0; + if (!half_panel_partial_update) + dsc_common_mode |= DSC_MODE_SPLIT_PANEL | DSC_MODE_MULTIPLEX; + if (enc_master->intf_mode == INTF_MODE_VIDEO) + dsc_common_mode |= DSC_MODE_VIDEO; + + _sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h); + + this_frame_slices = roi->w / dsc->slice_width; + intf_ip_w = this_frame_slices * dsc->slice_width; + _sde_encoder_dsc_pclk_param_calc(dsc, intf_ip_w); + + /* + * dsc merge case: when using 2 encoders for the same stream, + * no. of slices need to be same on both the encoders. + */ + enc_ip_w = intf_ip_w / 2; + _sde_encoder_dsc_initial_line_calc(dsc, enc_ip_w); + + ich_res = _sde_encoder_dsc_ich_reset_override_needed( + half_panel_partial_update, dsc); + + SDE_DEBUG_DCE(sde_enc, "pic_w: %d pic_h: %d mode:%d\n", + roi->w, roi->h, dsc_common_mode); + SDE_EVT32(DRMID(&sde_enc->base), roi->w, roi->h, + dsc_common_mode, i, params->affected_displays); + + _sde_encoder_dsc_pipe_cfg(hw_dsc[0], hw_pp[0], dsc, dsc_common_mode, + ich_res, true, hw_dsc_pp[0]); + cfg.dsc[0] = hw_dsc[0]->idx; + cfg.dsc_count++; + if (hw_ctl->ops.update_bitmask_dsc) + hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc[0]->idx, 1); + + + _sde_encoder_dsc_pipe_cfg(hw_dsc[1], hw_pp[1], dsc, dsc_common_mode, + ich_res, !half_panel_partial_update, hw_dsc_pp[1]); + if (!half_panel_partial_update) { + cfg.dsc[1] = hw_dsc[1]->idx; + cfg.dsc_count++; + if (hw_ctl->ops.update_bitmask_dsc) + hw_ctl->ops.update_bitmask_dsc(hw_ctl, hw_dsc[1]->idx, + 1); + } + /* setup dsc active configuration in the control path */ + if (hw_ctl->ops.setup_dsc_cfg) { + hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); + SDE_DEBUG_DCE(sde_enc, + "setup_dsc_cfg hw_ctl[%d], count:%d,dsc[0]:%d, dsc[1]:%d\n", + hw_ctl->idx, + cfg.dsc_count, + cfg.dsc[0], + cfg.dsc[1]); + } + return 0; +} + +int sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc, + struct sde_encoder_kickoff_params *params) +{ + enum sde_rm_topology_name topology; + struct drm_connector *drm_conn; + int ret = 0; + + if (!sde_enc || !params || !sde_enc->phys_encs[0] || + !sde_enc->phys_encs[0]->connector) + return -EINVAL; + + drm_conn = sde_enc->phys_encs[0]->connector; + + topology = sde_connector_get_topology_name(drm_conn); + if (topology == SDE_RM_TOPOLOGY_NONE) { + SDE_ERROR_DCE(sde_enc, "topology not set yet\n"); + return -EINVAL; + } + + SDE_DEBUG_DCE(sde_enc, "topology:%d\n", topology); + SDE_EVT32(DRMID(&sde_enc->base), topology, + sde_enc->cur_conn_roi.x, + sde_enc->cur_conn_roi.y, + sde_enc->cur_conn_roi.w, + sde_enc->cur_conn_roi.h, + sde_enc->prv_conn_roi.x, + sde_enc->prv_conn_roi.y, + sde_enc->prv_conn_roi.w, + sde_enc->prv_conn_roi.h, + sde_enc->cur_master->cached_mode.hdisplay, + sde_enc->cur_master->cached_mode.vdisplay); + + if (sde_kms_rect_is_equal(&sde_enc->cur_conn_roi, + &sde_enc->prv_conn_roi)) + return ret; + + switch (topology) { + case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC: + case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC: + ret = _sde_encoder_dsc_n_lm_1_enc_1_intf(sde_enc); + break; + case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE: + ret = _sde_encoder_dsc_2_lm_2_enc_1_intf(sde_enc, params); + break; + case SDE_RM_TOPOLOGY_DUALPIPE_DSC: + ret = _sde_encoder_dsc_2_lm_2_enc_2_intf(sde_enc, params); + break; + default: + SDE_ERROR_DCE(sde_enc, "No DSC support for topology %d", + topology); + return -EINVAL; + } + + return ret; +} + +void sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc) +{ + int i; + struct sde_hw_pingpong *hw_pp = NULL; + struct sde_hw_pingpong *hw_dsc_pp = NULL; + struct sde_hw_dsc *hw_dsc = NULL; + struct sde_hw_ctl *hw_ctl = NULL; + struct sde_ctl_dsc_cfg cfg; + + if (!sde_enc || !sde_enc->phys_encs[0] || + !sde_enc->phys_encs[0]->connector) { + SDE_ERROR("invalid params %d %d\n", + !sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1); + return; + } + + if (sde_enc->cur_master) + hw_ctl = sde_enc->cur_master->hw_ctl; + + /* Disable DSC for all the pp's present in this topology */ + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_pp = sde_enc->hw_pp[i]; + hw_dsc = sde_enc->hw_dsc[i]; + hw_dsc_pp = sde_enc->hw_dsc_pp[i]; + + _sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, NULL, + 0, 0, 0, hw_dsc_pp); + + if (hw_dsc) + sde_enc->dirty_dsc_ids[i] = hw_dsc->idx; + } + + /* Clear the DSC ACTIVE config for this CTL */ + if (hw_ctl && hw_ctl->ops.setup_dsc_cfg) { + memset(&cfg, 0, sizeof(cfg)); + hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg); + } + + /** + * Since pending flushes from previous commit get cleared + * sometime after this point, setting DSC flush bits now + * will have no effect. Therefore dirty_dsc_ids track which + * DSC blocks must be flushed for the next trigger. + */ +} + +bool sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc) +{ + int i; + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + /** + * This dirty_dsc_hw field is set during DSC disable to + * indicate which DSC blocks need to be flushed + */ + if (sde_enc->dirty_dsc_ids[i]) + return true; + } + + return false; +} + + +void sde_encoder_dsc_helper_flush_dsc(struct sde_encoder_virt *sde_enc) +{ + int i; + struct sde_hw_ctl *hw_ctl = NULL; + enum sde_dsc dsc_idx; + + if (sde_enc->cur_master) + hw_ctl = sde_enc->cur_master->hw_ctl; + + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + dsc_idx = sde_enc->dirty_dsc_ids[i]; + if (dsc_idx && hw_ctl && hw_ctl->ops.update_bitmask_dsc) + hw_ctl->ops.update_bitmask_dsc(hw_ctl, dsc_idx, 1); + + sde_enc->dirty_dsc_ids[i] = DSC_NONE; + } +} + + diff --git a/msm/sde/sde_encoder_dce.h b/msm/sde/sde_encoder_dce.h new file mode 100644 index 0000000000..ff325320f0 --- /dev/null +++ b/msm/sde/sde_encoder_dce.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __SDE_ENCODER_DCE_H__ +#define __SDE_ENCODER_DCE_H__ + +#include "sde_encoder.h" + +bool sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc); + +void sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc); + +int sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc, + struct sde_encoder_kickoff_params *params); + +void sde_encoder_dsc_helper_flush_dsc(struct sde_encoder_virt *sde_enc); + +#endif /* __SDE_ENCODER_DCE_H__ */ diff --git a/msm/sde/sde_kms.h b/msm/sde/sde_kms.h index bd0eb52054..56c0d23eae 100644 --- a/msm/sde/sde_kms.h +++ b/msm/sde/sde_kms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -232,6 +232,16 @@ struct sde_irq { struct dentry *debugfs_file; }; +/** + * struct sde_kms_frame_event_cb_data : info of drm objects of a frame event + * @crtc: pointer to drm crtc object registered for frame event + * @connector: pointer to drm connector which is source of frame event + */ +struct sde_kms_frame_event_cb_data { + struct drm_crtc *crtc; + struct drm_connector *connector; +}; + struct sde_kms { struct msm_kms base; struct drm_device *dev;