1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 225fdd593SJeykumar Sankaran /* 325fdd593SJeykumar Sankaran * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. 425fdd593SJeykumar Sankaran * Copyright (C) 2013 Red Hat 525fdd593SJeykumar Sankaran * Author: Rob Clark <robdclark@gmail.com> 625fdd593SJeykumar Sankaran */ 725fdd593SJeykumar Sankaran 825fdd593SJeykumar Sankaran #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ 925fdd593SJeykumar Sankaran #include <linux/sort.h> 1025fdd593SJeykumar Sankaran #include <linux/debugfs.h> 1125fdd593SJeykumar Sankaran #include <linux/ktime.h> 124259ff7aSKalyan Thota #include <linux/bits.h> 13feea39a8SSam Ravnborg 14351f950dSMaxime Ripard #include <drm/drm_atomic.h> 1525fdd593SJeykumar Sankaran #include <drm/drm_crtc.h> 1625fdd593SJeykumar Sankaran #include <drm/drm_flip_work.h> 17fcd70cd3SDaniel Vetter #include <drm/drm_mode.h> 18fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 1925fdd593SJeykumar Sankaran #include <drm/drm_rect.h> 20feea39a8SSam Ravnborg #include <drm/drm_vblank.h> 2125fdd593SJeykumar Sankaran 2225fdd593SJeykumar Sankaran #include "dpu_kms.h" 2325fdd593SJeykumar Sankaran #include "dpu_hw_lm.h" 2425fdd593SJeykumar Sankaran #include "dpu_hw_ctl.h" 254259ff7aSKalyan Thota #include "dpu_hw_dspp.h" 2625fdd593SJeykumar Sankaran #include "dpu_crtc.h" 2725fdd593SJeykumar Sankaran #include "dpu_plane.h" 2825fdd593SJeykumar Sankaran #include "dpu_encoder.h" 2925fdd593SJeykumar Sankaran #include "dpu_vbif.h" 3025fdd593SJeykumar Sankaran #include "dpu_core_perf.h" 3125fdd593SJeykumar Sankaran #include "dpu_trace.h" 3225fdd593SJeykumar Sankaran 3325fdd593SJeykumar Sankaran /* layer mixer index on dpu_crtc */ 3425fdd593SJeykumar Sankaran #define LEFT_MIXER 0 3525fdd593SJeykumar Sankaran #define RIGHT_MIXER 1 3625fdd593SJeykumar Sankaran 3770df9610SSean Paul /* timeout in ms waiting for frame done */ 3870df9610SSean Paul #define DPU_CRTC_FRAME_DONE_TIMEOUT_MS 60 3970df9610SSean Paul 404259ff7aSKalyan Thota #define CONVERT_S3_15(val) \ 414259ff7aSKalyan Thota (((((u64)val) & ~BIT_ULL(63)) >> 17) & GENMASK_ULL(17, 0)) 424259ff7aSKalyan Thota 4358fba464SSean Paul static struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc) 4425fdd593SJeykumar Sankaran { 4504b96b63SBruce Wang struct msm_drm_private *priv = crtc->dev->dev_private; 4625fdd593SJeykumar Sankaran 4725fdd593SJeykumar Sankaran return to_dpu_kms(priv->kms); 4825fdd593SJeykumar Sankaran } 4925fdd593SJeykumar Sankaran 5025fdd593SJeykumar Sankaran static void dpu_crtc_destroy(struct drm_crtc *crtc) 5125fdd593SJeykumar Sankaran { 5225fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 5325fdd593SJeykumar Sankaran 5425fdd593SJeykumar Sankaran if (!crtc) 5525fdd593SJeykumar Sankaran return; 5625fdd593SJeykumar Sankaran 5725fdd593SJeykumar Sankaran drm_crtc_cleanup(crtc); 5825fdd593SJeykumar Sankaran kfree(dpu_crtc); 5925fdd593SJeykumar Sankaran } 6025fdd593SJeykumar Sankaran 6173743e72SKalyan Thota static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc) 6273743e72SKalyan Thota { 6373743e72SKalyan Thota struct drm_device *dev = crtc->dev; 6473743e72SKalyan Thota struct drm_encoder *encoder; 6573743e72SKalyan Thota 6673743e72SKalyan Thota drm_for_each_encoder(encoder, dev) 6773743e72SKalyan Thota if (encoder->crtc == crtc) 6873743e72SKalyan Thota return encoder; 6973743e72SKalyan Thota 7073743e72SKalyan Thota return NULL; 7173743e72SKalyan Thota } 7273743e72SKalyan Thota 7373743e72SKalyan Thota static u32 dpu_crtc_get_vblank_counter(struct drm_crtc *crtc) 7473743e72SKalyan Thota { 75*885455d6SMark Yacoub struct drm_encoder *encoder = get_encoder_from_crtc(crtc); 7673743e72SKalyan Thota if (!encoder) { 7773743e72SKalyan Thota DRM_ERROR("no encoder found for crtc %d\n", crtc->index); 78*885455d6SMark Yacoub return 0; 7973743e72SKalyan Thota } 8073743e72SKalyan Thota 81*885455d6SMark Yacoub return dpu_encoder_get_vsync_count(encoder); 8273743e72SKalyan Thota } 8373743e72SKalyan Thota 8473743e72SKalyan Thota static bool dpu_crtc_get_scanout_position(struct drm_crtc *crtc, 8573743e72SKalyan Thota bool in_vblank_irq, 8673743e72SKalyan Thota int *vpos, int *hpos, 8773743e72SKalyan Thota ktime_t *stime, ktime_t *etime, 8873743e72SKalyan Thota const struct drm_display_mode *mode) 8973743e72SKalyan Thota { 9073743e72SKalyan Thota unsigned int pipe = crtc->index; 9173743e72SKalyan Thota struct drm_encoder *encoder; 9273743e72SKalyan Thota int line, vsw, vbp, vactive_start, vactive_end, vfp_end; 9373743e72SKalyan Thota 9473743e72SKalyan Thota encoder = get_encoder_from_crtc(crtc); 9573743e72SKalyan Thota if (!encoder) { 9673743e72SKalyan Thota DRM_ERROR("no encoder found for crtc %d\n", pipe); 9773743e72SKalyan Thota return false; 9873743e72SKalyan Thota } 9973743e72SKalyan Thota 10073743e72SKalyan Thota vsw = mode->crtc_vsync_end - mode->crtc_vsync_start; 10173743e72SKalyan Thota vbp = mode->crtc_vtotal - mode->crtc_vsync_end; 10273743e72SKalyan Thota 10373743e72SKalyan Thota /* 10473743e72SKalyan Thota * the line counter is 1 at the start of the VSYNC pulse and VTOTAL at 10573743e72SKalyan Thota * the end of VFP. Translate the porch values relative to the line 10673743e72SKalyan Thota * counter positions. 10773743e72SKalyan Thota */ 10873743e72SKalyan Thota 10973743e72SKalyan Thota vactive_start = vsw + vbp + 1; 11073743e72SKalyan Thota vactive_end = vactive_start + mode->crtc_vdisplay; 11173743e72SKalyan Thota 11273743e72SKalyan Thota /* last scan line before VSYNC */ 11373743e72SKalyan Thota vfp_end = mode->crtc_vtotal; 11473743e72SKalyan Thota 11573743e72SKalyan Thota if (stime) 11673743e72SKalyan Thota *stime = ktime_get(); 11773743e72SKalyan Thota 11873743e72SKalyan Thota line = dpu_encoder_get_linecount(encoder); 11973743e72SKalyan Thota 12073743e72SKalyan Thota if (line < vactive_start) 12173743e72SKalyan Thota line -= vactive_start; 12273743e72SKalyan Thota else if (line > vactive_end) 12373743e72SKalyan Thota line = line - vfp_end - vactive_start; 12473743e72SKalyan Thota else 12573743e72SKalyan Thota line -= vactive_start; 12673743e72SKalyan Thota 12773743e72SKalyan Thota *vpos = line; 12873743e72SKalyan Thota *hpos = 0; 12973743e72SKalyan Thota 13073743e72SKalyan Thota if (etime) 13173743e72SKalyan Thota *etime = ktime_get(); 13273743e72SKalyan Thota 13373743e72SKalyan Thota return true; 13473743e72SKalyan Thota } 13573743e72SKalyan Thota 13625fdd593SJeykumar Sankaran static void _dpu_crtc_setup_blend_cfg(struct dpu_crtc_mixer *mixer, 13774593a28SSravanthi Kollukuduru struct dpu_plane_state *pstate, struct dpu_format *format) 13825fdd593SJeykumar Sankaran { 13925fdd593SJeykumar Sankaran struct dpu_hw_mixer *lm = mixer->hw_lm; 14074593a28SSravanthi Kollukuduru uint32_t blend_op; 141f964cfb7SDmitry Baryshkov uint32_t fg_alpha, bg_alpha; 142f964cfb7SDmitry Baryshkov 143f964cfb7SDmitry Baryshkov fg_alpha = pstate->base.alpha >> 8; 144f964cfb7SDmitry Baryshkov bg_alpha = 0xff - fg_alpha; 14525fdd593SJeykumar Sankaran 14625fdd593SJeykumar Sankaran /* default to opaque blending */ 147f964cfb7SDmitry Baryshkov if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE || 148f964cfb7SDmitry Baryshkov !format->alpha_enable) { 14974593a28SSravanthi Kollukuduru blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | 15074593a28SSravanthi Kollukuduru DPU_BLEND_BG_ALPHA_BG_CONST; 151f964cfb7SDmitry Baryshkov } else if (pstate->base.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) { 152f964cfb7SDmitry Baryshkov blend_op = DPU_BLEND_FG_ALPHA_FG_CONST | 153f964cfb7SDmitry Baryshkov DPU_BLEND_BG_ALPHA_FG_PIXEL; 154f964cfb7SDmitry Baryshkov if (fg_alpha != 0xff) { 155f964cfb7SDmitry Baryshkov bg_alpha = fg_alpha; 156f964cfb7SDmitry Baryshkov blend_op |= DPU_BLEND_BG_MOD_ALPHA | 157f964cfb7SDmitry Baryshkov DPU_BLEND_BG_INV_MOD_ALPHA; 158f964cfb7SDmitry Baryshkov } else { 159f964cfb7SDmitry Baryshkov blend_op |= DPU_BLEND_BG_INV_ALPHA; 160f964cfb7SDmitry Baryshkov } 161f964cfb7SDmitry Baryshkov } else { 16274593a28SSravanthi Kollukuduru /* coverage blending */ 16374593a28SSravanthi Kollukuduru blend_op = DPU_BLEND_FG_ALPHA_FG_PIXEL | 164f964cfb7SDmitry Baryshkov DPU_BLEND_BG_ALPHA_FG_PIXEL; 165f964cfb7SDmitry Baryshkov if (fg_alpha != 0xff) { 166f964cfb7SDmitry Baryshkov bg_alpha = fg_alpha; 167f964cfb7SDmitry Baryshkov blend_op |= DPU_BLEND_FG_MOD_ALPHA | 168f964cfb7SDmitry Baryshkov DPU_BLEND_FG_INV_MOD_ALPHA | 169f964cfb7SDmitry Baryshkov DPU_BLEND_BG_MOD_ALPHA | 170f964cfb7SDmitry Baryshkov DPU_BLEND_BG_INV_MOD_ALPHA; 171f964cfb7SDmitry Baryshkov } else { 172f964cfb7SDmitry Baryshkov blend_op |= DPU_BLEND_BG_INV_ALPHA; 173f964cfb7SDmitry Baryshkov } 17474593a28SSravanthi Kollukuduru } 17574593a28SSravanthi Kollukuduru 17674593a28SSravanthi Kollukuduru lm->ops.setup_blend_config(lm, pstate->stage, 177f964cfb7SDmitry Baryshkov fg_alpha, bg_alpha, blend_op); 17874593a28SSravanthi Kollukuduru 1795b702d78SStephen Boyd DRM_DEBUG_ATOMIC("format:%p4cc, alpha_en:%u blend_op:0x%x\n", 18092f1d09cSSakari Ailus &format->base.pixel_format, format->alpha_enable, blend_op); 18125fdd593SJeykumar Sankaran } 18225fdd593SJeykumar Sankaran 18325fdd593SJeykumar Sankaran static void _dpu_crtc_program_lm_output_roi(struct drm_crtc *crtc) 18425fdd593SJeykumar Sankaran { 18525fdd593SJeykumar Sankaran struct dpu_crtc_state *crtc_state; 18625fdd593SJeykumar Sankaran int lm_idx, lm_horiz_position; 18725fdd593SJeykumar Sankaran 18825fdd593SJeykumar Sankaran crtc_state = to_dpu_crtc_state(crtc->state); 18925fdd593SJeykumar Sankaran 19025fdd593SJeykumar Sankaran lm_horiz_position = 0; 1919222cdd2SJeykumar Sankaran for (lm_idx = 0; lm_idx < crtc_state->num_mixers; lm_idx++) { 19225fdd593SJeykumar Sankaran const struct drm_rect *lm_roi = &crtc_state->lm_bounds[lm_idx]; 1939222cdd2SJeykumar Sankaran struct dpu_hw_mixer *hw_lm = crtc_state->mixers[lm_idx].hw_lm; 19425fdd593SJeykumar Sankaran struct dpu_hw_mixer_cfg cfg; 19525fdd593SJeykumar Sankaran 19625fdd593SJeykumar Sankaran if (!lm_roi || !drm_rect_visible(lm_roi)) 19725fdd593SJeykumar Sankaran continue; 19825fdd593SJeykumar Sankaran 19925fdd593SJeykumar Sankaran cfg.out_width = drm_rect_width(lm_roi); 20025fdd593SJeykumar Sankaran cfg.out_height = drm_rect_height(lm_roi); 20125fdd593SJeykumar Sankaran cfg.right_mixer = lm_horiz_position++; 20225fdd593SJeykumar Sankaran cfg.flags = 0; 20325fdd593SJeykumar Sankaran hw_lm->ops.setup_mixer_out(hw_lm, &cfg); 20425fdd593SJeykumar Sankaran } 20525fdd593SJeykumar Sankaran } 20625fdd593SJeykumar Sankaran 20725fdd593SJeykumar Sankaran static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc, 20825fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc, struct dpu_crtc_mixer *mixer) 20925fdd593SJeykumar Sankaran { 21025fdd593SJeykumar Sankaran struct drm_plane *plane; 21125fdd593SJeykumar Sankaran struct drm_framebuffer *fb; 21225fdd593SJeykumar Sankaran struct drm_plane_state *state; 21304b96b63SBruce Wang struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 21425fdd593SJeykumar Sankaran struct dpu_plane_state *pstate = NULL; 21525fdd593SJeykumar Sankaran struct dpu_format *format; 21604b96b63SBruce Wang struct dpu_hw_ctl *ctl = mixer->lm_ctl; 21704b96b63SBruce Wang struct dpu_hw_stage_cfg *stage_cfg = &dpu_crtc->stage_cfg; 21825fdd593SJeykumar Sankaran 21925fdd593SJeykumar Sankaran u32 flush_mask; 22025fdd593SJeykumar Sankaran uint32_t stage_idx, lm_idx; 22125fdd593SJeykumar Sankaran int zpos_cnt[DPU_STAGE_MAX + 1] = { 0 }; 22225fdd593SJeykumar Sankaran bool bg_alpha_enable = false; 223b3652e87SKrishna Manikandan DECLARE_BITMAP(fetch_active, SSPP_MAX); 22425fdd593SJeykumar Sankaran 225b3652e87SKrishna Manikandan memset(fetch_active, 0, sizeof(fetch_active)); 22625fdd593SJeykumar Sankaran drm_atomic_crtc_for_each_plane(plane, crtc) { 22725fdd593SJeykumar Sankaran state = plane->state; 22825fdd593SJeykumar Sankaran if (!state) 22925fdd593SJeykumar Sankaran continue; 23025fdd593SJeykumar Sankaran 23125fdd593SJeykumar Sankaran pstate = to_dpu_plane_state(state); 23225fdd593SJeykumar Sankaran fb = state->fb; 23325fdd593SJeykumar Sankaran 23425fdd593SJeykumar Sankaran dpu_plane_get_ctl_flush(plane, ctl, &flush_mask); 235b3652e87SKrishna Manikandan set_bit(dpu_plane_pipe(plane), fetch_active); 2365b702d78SStephen Boyd 2375b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc %d stage:%d - plane %d sspp %d fb %d\n", 23825fdd593SJeykumar Sankaran crtc->base.id, 23925fdd593SJeykumar Sankaran pstate->stage, 24025fdd593SJeykumar Sankaran plane->base.id, 24125fdd593SJeykumar Sankaran dpu_plane_pipe(plane) - SSPP_VIG0, 24225fdd593SJeykumar Sankaran state->fb ? state->fb->base.id : -1); 24325fdd593SJeykumar Sankaran 24425fdd593SJeykumar Sankaran format = to_dpu_format(msm_framebuffer_format(pstate->base.fb)); 24525fdd593SJeykumar Sankaran 24625fdd593SJeykumar Sankaran if (pstate->stage == DPU_STAGE_BASE && format->alpha_enable) 24725fdd593SJeykumar Sankaran bg_alpha_enable = true; 24825fdd593SJeykumar Sankaran 24925fdd593SJeykumar Sankaran stage_idx = zpos_cnt[pstate->stage]++; 25025fdd593SJeykumar Sankaran stage_cfg->stage[pstate->stage][stage_idx] = 25125fdd593SJeykumar Sankaran dpu_plane_pipe(plane); 25225fdd593SJeykumar Sankaran stage_cfg->multirect_index[pstate->stage][stage_idx] = 25325fdd593SJeykumar Sankaran pstate->multirect_index; 25425fdd593SJeykumar Sankaran 25525fdd593SJeykumar Sankaran trace_dpu_crtc_setup_mixer(DRMID(crtc), DRMID(plane), 25625fdd593SJeykumar Sankaran state, pstate, stage_idx, 25725fdd593SJeykumar Sankaran dpu_plane_pipe(plane) - SSPP_VIG0, 25825fdd593SJeykumar Sankaran format->base.pixel_format, 25925fdd593SJeykumar Sankaran fb ? fb->modifier : 0); 26025fdd593SJeykumar Sankaran 26125fdd593SJeykumar Sankaran /* blend config update */ 2629222cdd2SJeykumar Sankaran for (lm_idx = 0; lm_idx < cstate->num_mixers; lm_idx++) { 26374593a28SSravanthi Kollukuduru _dpu_crtc_setup_blend_cfg(mixer + lm_idx, 26474593a28SSravanthi Kollukuduru pstate, format); 26525fdd593SJeykumar Sankaran 26625fdd593SJeykumar Sankaran mixer[lm_idx].flush_mask |= flush_mask; 26725fdd593SJeykumar Sankaran 26825fdd593SJeykumar Sankaran if (bg_alpha_enable && !format->alpha_enable) 26925fdd593SJeykumar Sankaran mixer[lm_idx].mixer_op_mode = 0; 27025fdd593SJeykumar Sankaran else 27125fdd593SJeykumar Sankaran mixer[lm_idx].mixer_op_mode |= 27225fdd593SJeykumar Sankaran 1 << pstate->stage; 27325fdd593SJeykumar Sankaran } 27425fdd593SJeykumar Sankaran } 27525fdd593SJeykumar Sankaran 276b3652e87SKrishna Manikandan if (ctl->ops.set_active_pipes) 277b3652e87SKrishna Manikandan ctl->ops.set_active_pipes(ctl, fetch_active); 278b3652e87SKrishna Manikandan 27925fdd593SJeykumar Sankaran _dpu_crtc_program_lm_output_roi(crtc); 28025fdd593SJeykumar Sankaran } 28125fdd593SJeykumar Sankaran 28225fdd593SJeykumar Sankaran /** 28325fdd593SJeykumar Sankaran * _dpu_crtc_blend_setup - configure crtc mixers 28425fdd593SJeykumar Sankaran * @crtc: Pointer to drm crtc structure 28525fdd593SJeykumar Sankaran */ 28625fdd593SJeykumar Sankaran static void _dpu_crtc_blend_setup(struct drm_crtc *crtc) 28725fdd593SJeykumar Sankaran { 28804b96b63SBruce Wang struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 28904b96b63SBruce Wang struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 29004b96b63SBruce Wang struct dpu_crtc_mixer *mixer = cstate->mixers; 29125fdd593SJeykumar Sankaran struct dpu_hw_ctl *ctl; 29225fdd593SJeykumar Sankaran struct dpu_hw_mixer *lm; 29325fdd593SJeykumar Sankaran int i; 29425fdd593SJeykumar Sankaran 2955b702d78SStephen Boyd DRM_DEBUG_ATOMIC("%s\n", dpu_crtc->name); 29625fdd593SJeykumar Sankaran 2979222cdd2SJeykumar Sankaran for (i = 0; i < cstate->num_mixers; i++) { 29825fdd593SJeykumar Sankaran mixer[i].mixer_op_mode = 0; 29925fdd593SJeykumar Sankaran mixer[i].flush_mask = 0; 300cf6916f4SJeykumar Sankaran if (mixer[i].lm_ctl->ops.clear_all_blendstages) 301cf6916f4SJeykumar Sankaran mixer[i].lm_ctl->ops.clear_all_blendstages( 302cf6916f4SJeykumar Sankaran mixer[i].lm_ctl); 30325fdd593SJeykumar Sankaran } 30425fdd593SJeykumar Sankaran 30525fdd593SJeykumar Sankaran /* initialize stage cfg */ 30625fdd593SJeykumar Sankaran memset(&dpu_crtc->stage_cfg, 0, sizeof(struct dpu_hw_stage_cfg)); 30725fdd593SJeykumar Sankaran 30825fdd593SJeykumar Sankaran _dpu_crtc_blend_setup_mixer(crtc, dpu_crtc, mixer); 30925fdd593SJeykumar Sankaran 3109222cdd2SJeykumar Sankaran for (i = 0; i < cstate->num_mixers; i++) { 311cf6916f4SJeykumar Sankaran ctl = mixer[i].lm_ctl; 31225fdd593SJeykumar Sankaran lm = mixer[i].hw_lm; 31325fdd593SJeykumar Sankaran 31425fdd593SJeykumar Sankaran lm->ops.setup_alpha_out(lm, mixer[i].mixer_op_mode); 31525fdd593SJeykumar Sankaran 31625fdd593SJeykumar Sankaran mixer[i].flush_mask |= ctl->ops.get_bitmask_mixer(ctl, 31725fdd593SJeykumar Sankaran mixer[i].hw_lm->idx); 31825fdd593SJeykumar Sankaran 31925fdd593SJeykumar Sankaran /* stage config flush mask */ 32025fdd593SJeykumar Sankaran ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask); 32125fdd593SJeykumar Sankaran 3225b702d78SStephen Boyd DRM_DEBUG_ATOMIC("lm %d, op_mode 0x%X, ctl %d, flush mask 0x%x\n", 32325fdd593SJeykumar Sankaran mixer[i].hw_lm->idx - LM_0, 32425fdd593SJeykumar Sankaran mixer[i].mixer_op_mode, 32525fdd593SJeykumar Sankaran ctl->idx - CTL_0, 32625fdd593SJeykumar Sankaran mixer[i].flush_mask); 32725fdd593SJeykumar Sankaran 32825fdd593SJeykumar Sankaran ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, 32925fdd593SJeykumar Sankaran &dpu_crtc->stage_cfg); 33025fdd593SJeykumar Sankaran } 33125fdd593SJeykumar Sankaran } 33225fdd593SJeykumar Sankaran 33325fdd593SJeykumar Sankaran /** 33425fdd593SJeykumar Sankaran * _dpu_crtc_complete_flip - signal pending page_flip events 33525fdd593SJeykumar Sankaran * Any pending vblank events are added to the vblank_event_list 33625fdd593SJeykumar Sankaran * so that the next vblank interrupt shall signal them. 33725fdd593SJeykumar Sankaran * However PAGE_FLIP events are not handled through the vblank_event_list. 33825fdd593SJeykumar Sankaran * This API signals any pending PAGE_FLIP events requested through 33925fdd593SJeykumar Sankaran * DRM_IOCTL_MODE_PAGE_FLIP and are cached in the dpu_crtc->event. 34025fdd593SJeykumar Sankaran * @crtc: Pointer to drm crtc structure 34125fdd593SJeykumar Sankaran */ 34225fdd593SJeykumar Sankaran static void _dpu_crtc_complete_flip(struct drm_crtc *crtc) 34325fdd593SJeykumar Sankaran { 34425fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 34525fdd593SJeykumar Sankaran struct drm_device *dev = crtc->dev; 34625fdd593SJeykumar Sankaran unsigned long flags; 34725fdd593SJeykumar Sankaran 34825fdd593SJeykumar Sankaran spin_lock_irqsave(&dev->event_lock, flags); 34925fdd593SJeykumar Sankaran if (dpu_crtc->event) { 35025fdd593SJeykumar Sankaran DRM_DEBUG_VBL("%s: send event: %pK\n", dpu_crtc->name, 35125fdd593SJeykumar Sankaran dpu_crtc->event); 35225fdd593SJeykumar Sankaran trace_dpu_crtc_complete_flip(DRMID(crtc)); 35325fdd593SJeykumar Sankaran drm_crtc_send_vblank_event(crtc, dpu_crtc->event); 35425fdd593SJeykumar Sankaran dpu_crtc->event = NULL; 35525fdd593SJeykumar Sankaran } 35625fdd593SJeykumar Sankaran spin_unlock_irqrestore(&dev->event_lock, flags); 35725fdd593SJeykumar Sankaran } 35825fdd593SJeykumar Sankaran 35925fdd593SJeykumar Sankaran enum dpu_intf_mode dpu_crtc_get_intf_mode(struct drm_crtc *crtc) 36025fdd593SJeykumar Sankaran { 36125fdd593SJeykumar Sankaran struct drm_encoder *encoder; 36225fdd593SJeykumar Sankaran 363ab198a7aSSean Paul /* 364ab198a7aSSean Paul * TODO: This function is called from dpu debugfs and as part of atomic 365ab198a7aSSean Paul * check. When called from debugfs, the crtc->mutex must be held to 366ab198a7aSSean Paul * read crtc->state. However reading crtc->state from atomic check isn't 367ab198a7aSSean Paul * allowed (unless you have a good reason, a big comment, and a deep 368ab198a7aSSean Paul * understanding of how the atomic/modeset locks work (<- and this is 369ab198a7aSSean Paul * probably not possible)). So we'll keep the WARN_ON here for now, but 370ab198a7aSSean Paul * really we need to figure out a better way to track our operating mode 371ab198a7aSSean Paul */ 3721dfdb0e1SSean Paul WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); 3731dfdb0e1SSean Paul 3744b8c6279SSean Paul /* TODO: Returns the first INTF_MODE, could there be multiple values? */ 3754b8c6279SSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) 37625fdd593SJeykumar Sankaran return dpu_encoder_get_intf_mode(encoder); 37725fdd593SJeykumar Sankaran 37825fdd593SJeykumar Sankaran return INTF_MODE_NONE; 37925fdd593SJeykumar Sankaran } 38025fdd593SJeykumar Sankaran 381e4914867SSean Paul void dpu_crtc_vblank_callback(struct drm_crtc *crtc) 38225fdd593SJeykumar Sankaran { 38325fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 38425fdd593SJeykumar Sankaran 38525fdd593SJeykumar Sankaran /* keep statistics on vblank callback - with auto reset via debugfs */ 38625fdd593SJeykumar Sankaran if (ktime_compare(dpu_crtc->vblank_cb_time, ktime_set(0, 0)) == 0) 38725fdd593SJeykumar Sankaran dpu_crtc->vblank_cb_time = ktime_get(); 38825fdd593SJeykumar Sankaran else 38925fdd593SJeykumar Sankaran dpu_crtc->vblank_cb_count++; 39025fdd593SJeykumar Sankaran drm_crtc_handle_vblank(crtc); 39125fdd593SJeykumar Sankaran trace_dpu_crtc_vblank_cb(DRMID(crtc)); 39225fdd593SJeykumar Sankaran } 39325fdd593SJeykumar Sankaran 39425fdd593SJeykumar Sankaran static void dpu_crtc_frame_event_work(struct kthread_work *work) 39525fdd593SJeykumar Sankaran { 39604b96b63SBruce Wang struct dpu_crtc_frame_event *fevent = container_of(work, 39704b96b63SBruce Wang struct dpu_crtc_frame_event, work); 39804b96b63SBruce Wang struct drm_crtc *crtc = fevent->crtc; 39904b96b63SBruce Wang struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 40025fdd593SJeykumar Sankaran unsigned long flags; 40125fdd593SJeykumar Sankaran bool frame_done = false; 40225fdd593SJeykumar Sankaran 40325fdd593SJeykumar Sankaran DPU_ATRACE_BEGIN("crtc_frame_event"); 40425fdd593SJeykumar Sankaran 4055b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event, 40625fdd593SJeykumar Sankaran ktime_to_ns(fevent->ts)); 40725fdd593SJeykumar Sankaran 40825fdd593SJeykumar Sankaran if (fevent->event & (DPU_ENCODER_FRAME_EVENT_DONE 40925fdd593SJeykumar Sankaran | DPU_ENCODER_FRAME_EVENT_ERROR 41025fdd593SJeykumar Sankaran | DPU_ENCODER_FRAME_EVENT_PANEL_DEAD)) { 41125fdd593SJeykumar Sankaran 41225fdd593SJeykumar Sankaran if (atomic_read(&dpu_crtc->frame_pending) < 1) { 41341a52059SRob Clark /* ignore vblank when not pending */ 41425fdd593SJeykumar Sankaran } else if (atomic_dec_return(&dpu_crtc->frame_pending) == 0) { 41525fdd593SJeykumar Sankaran /* release bandwidth and other resources */ 41625fdd593SJeykumar Sankaran trace_dpu_crtc_frame_event_done(DRMID(crtc), 41725fdd593SJeykumar Sankaran fevent->event); 418241b507cSRob Clark dpu_core_perf_crtc_release_bw(crtc); 41925fdd593SJeykumar Sankaran } else { 42025fdd593SJeykumar Sankaran trace_dpu_crtc_frame_event_more_pending(DRMID(crtc), 42125fdd593SJeykumar Sankaran fevent->event); 42225fdd593SJeykumar Sankaran } 42325fdd593SJeykumar Sankaran 42425fdd593SJeykumar Sankaran if (fevent->event & (DPU_ENCODER_FRAME_EVENT_DONE 42525fdd593SJeykumar Sankaran | DPU_ENCODER_FRAME_EVENT_ERROR)) 42625fdd593SJeykumar Sankaran frame_done = true; 42725fdd593SJeykumar Sankaran } 42825fdd593SJeykumar Sankaran 42925fdd593SJeykumar Sankaran if (fevent->event & DPU_ENCODER_FRAME_EVENT_PANEL_DEAD) 43025fdd593SJeykumar Sankaran DPU_ERROR("crtc%d ts:%lld received panel dead event\n", 43125fdd593SJeykumar Sankaran crtc->base.id, ktime_to_ns(fevent->ts)); 43225fdd593SJeykumar Sankaran 43325fdd593SJeykumar Sankaran if (frame_done) 43425fdd593SJeykumar Sankaran complete_all(&dpu_crtc->frame_done_comp); 43525fdd593SJeykumar Sankaran 43625fdd593SJeykumar Sankaran spin_lock_irqsave(&dpu_crtc->spin_lock, flags); 43725fdd593SJeykumar Sankaran list_add_tail(&fevent->list, &dpu_crtc->frame_event_list); 43825fdd593SJeykumar Sankaran spin_unlock_irqrestore(&dpu_crtc->spin_lock, flags); 43925fdd593SJeykumar Sankaran DPU_ATRACE_END("crtc_frame_event"); 44025fdd593SJeykumar Sankaran } 44125fdd593SJeykumar Sankaran 44225fdd593SJeykumar Sankaran /* 44325fdd593SJeykumar Sankaran * dpu_crtc_frame_event_cb - crtc frame event callback API. CRTC module 44425fdd593SJeykumar Sankaran * registers this API to encoder for all frame event callbacks like 44525fdd593SJeykumar Sankaran * frame_error, frame_done, idle_timeout, etc. Encoder may call different events 44625fdd593SJeykumar Sankaran * from different context - IRQ, user thread, commit_thread, etc. Each event 44725fdd593SJeykumar Sankaran * should be carefully reviewed and should be processed in proper task context 44825fdd593SJeykumar Sankaran * to avoid schedulin delay or properly manage the irq context's bottom half 44925fdd593SJeykumar Sankaran * processing. 45025fdd593SJeykumar Sankaran */ 45125fdd593SJeykumar Sankaran static void dpu_crtc_frame_event_cb(void *data, u32 event) 45225fdd593SJeykumar Sankaran { 45325fdd593SJeykumar Sankaran struct drm_crtc *crtc = (struct drm_crtc *)data; 45425fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc; 45525fdd593SJeykumar Sankaran struct msm_drm_private *priv; 45625fdd593SJeykumar Sankaran struct dpu_crtc_frame_event *fevent; 45725fdd593SJeykumar Sankaran unsigned long flags; 45825fdd593SJeykumar Sankaran u32 crtc_id; 45925fdd593SJeykumar Sankaran 46025fdd593SJeykumar Sankaran /* Nothing to do on idle event */ 46125fdd593SJeykumar Sankaran if (event & DPU_ENCODER_FRAME_EVENT_IDLE) 46225fdd593SJeykumar Sankaran return; 46325fdd593SJeykumar Sankaran 46425fdd593SJeykumar Sankaran dpu_crtc = to_dpu_crtc(crtc); 46525fdd593SJeykumar Sankaran priv = crtc->dev->dev_private; 46625fdd593SJeykumar Sankaran crtc_id = drm_crtc_index(crtc); 46725fdd593SJeykumar Sankaran 46825fdd593SJeykumar Sankaran trace_dpu_crtc_frame_event_cb(DRMID(crtc), event); 46925fdd593SJeykumar Sankaran 47025fdd593SJeykumar Sankaran spin_lock_irqsave(&dpu_crtc->spin_lock, flags); 47125fdd593SJeykumar Sankaran fevent = list_first_entry_or_null(&dpu_crtc->frame_event_list, 47225fdd593SJeykumar Sankaran struct dpu_crtc_frame_event, list); 47325fdd593SJeykumar Sankaran if (fevent) 47425fdd593SJeykumar Sankaran list_del_init(&fevent->list); 47525fdd593SJeykumar Sankaran spin_unlock_irqrestore(&dpu_crtc->spin_lock, flags); 47625fdd593SJeykumar Sankaran 47725fdd593SJeykumar Sankaran if (!fevent) { 4785e16372bSRob Clark DRM_ERROR_RATELIMITED("crtc%d event %d overflow\n", crtc->base.id, event); 47925fdd593SJeykumar Sankaran return; 48025fdd593SJeykumar Sankaran } 48125fdd593SJeykumar Sankaran 48225fdd593SJeykumar Sankaran fevent->event = event; 48325fdd593SJeykumar Sankaran fevent->crtc = crtc; 48425fdd593SJeykumar Sankaran fevent->ts = ktime_get(); 4851041dee2SBernard kthread_queue_work(priv->event_thread[crtc_id].worker, &fevent->work); 48625fdd593SJeykumar Sankaran } 48725fdd593SJeykumar Sankaran 48880b4b4a7SRob Clark void dpu_crtc_complete_commit(struct drm_crtc *crtc) 48925fdd593SJeykumar Sankaran { 49025fdd593SJeykumar Sankaran trace_dpu_crtc_complete_commit(DRMID(crtc)); 491a1f2ba60SKrishna Manikandan dpu_core_perf_crtc_update(crtc, 0, false); 492fd630ae9SRob Clark _dpu_crtc_complete_flip(crtc); 49325fdd593SJeykumar Sankaran } 49425fdd593SJeykumar Sankaran 49525fdd593SJeykumar Sankaran static void _dpu_crtc_setup_lm_bounds(struct drm_crtc *crtc, 49625fdd593SJeykumar Sankaran struct drm_crtc_state *state) 49725fdd593SJeykumar Sankaran { 49804b96b63SBruce Wang struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); 49904b96b63SBruce Wang struct drm_display_mode *adj_mode = &state->adjusted_mode; 5003804a982SJordan Crouse u32 crtc_split_width = adj_mode->hdisplay / cstate->num_mixers; 50125fdd593SJeykumar Sankaran int i; 50225fdd593SJeykumar Sankaran 5039222cdd2SJeykumar Sankaran for (i = 0; i < cstate->num_mixers; i++) { 50425fdd593SJeykumar Sankaran struct drm_rect *r = &cstate->lm_bounds[i]; 50525fdd593SJeykumar Sankaran r->x1 = crtc_split_width * i; 50625fdd593SJeykumar Sankaran r->y1 = 0; 50725fdd593SJeykumar Sankaran r->x2 = r->x1 + crtc_split_width; 50835d600ddSJordan Crouse r->y2 = adj_mode->vdisplay; 50925fdd593SJeykumar Sankaran 51025fdd593SJeykumar Sankaran trace_dpu_crtc_setup_lm_bounds(DRMID(crtc), i, r); 51125fdd593SJeykumar Sankaran } 51225fdd593SJeykumar Sankaran } 51325fdd593SJeykumar Sankaran 5144259ff7aSKalyan Thota static void _dpu_crtc_get_pcc_coeff(struct drm_crtc_state *state, 5154259ff7aSKalyan Thota struct dpu_hw_pcc_cfg *cfg) 5164259ff7aSKalyan Thota { 5174259ff7aSKalyan Thota struct drm_color_ctm *ctm; 5184259ff7aSKalyan Thota 5194259ff7aSKalyan Thota memset(cfg, 0, sizeof(struct dpu_hw_pcc_cfg)); 5204259ff7aSKalyan Thota 5214259ff7aSKalyan Thota ctm = (struct drm_color_ctm *)state->ctm->data; 5224259ff7aSKalyan Thota 5234259ff7aSKalyan Thota if (!ctm) 5244259ff7aSKalyan Thota return; 5254259ff7aSKalyan Thota 5264259ff7aSKalyan Thota cfg->r.r = CONVERT_S3_15(ctm->matrix[0]); 5274259ff7aSKalyan Thota cfg->g.r = CONVERT_S3_15(ctm->matrix[1]); 5284259ff7aSKalyan Thota cfg->b.r = CONVERT_S3_15(ctm->matrix[2]); 5294259ff7aSKalyan Thota 5304259ff7aSKalyan Thota cfg->r.g = CONVERT_S3_15(ctm->matrix[3]); 5314259ff7aSKalyan Thota cfg->g.g = CONVERT_S3_15(ctm->matrix[4]); 5324259ff7aSKalyan Thota cfg->b.g = CONVERT_S3_15(ctm->matrix[5]); 5334259ff7aSKalyan Thota 5344259ff7aSKalyan Thota cfg->r.b = CONVERT_S3_15(ctm->matrix[6]); 5354259ff7aSKalyan Thota cfg->g.b = CONVERT_S3_15(ctm->matrix[7]); 5364259ff7aSKalyan Thota cfg->b.b = CONVERT_S3_15(ctm->matrix[8]); 5374259ff7aSKalyan Thota } 5384259ff7aSKalyan Thota 5394259ff7aSKalyan Thota static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc) 5404259ff7aSKalyan Thota { 5414259ff7aSKalyan Thota struct drm_crtc_state *state = crtc->state; 5424259ff7aSKalyan Thota struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 5434259ff7aSKalyan Thota struct dpu_crtc_mixer *mixer = cstate->mixers; 5444259ff7aSKalyan Thota struct dpu_hw_pcc_cfg cfg; 5454259ff7aSKalyan Thota struct dpu_hw_ctl *ctl; 5464259ff7aSKalyan Thota struct dpu_hw_dspp *dspp; 5474259ff7aSKalyan Thota int i; 5484259ff7aSKalyan Thota 5494259ff7aSKalyan Thota 5504259ff7aSKalyan Thota if (!state->color_mgmt_changed) 5514259ff7aSKalyan Thota return; 5524259ff7aSKalyan Thota 5534259ff7aSKalyan Thota for (i = 0; i < cstate->num_mixers; i++) { 5544259ff7aSKalyan Thota ctl = mixer[i].lm_ctl; 5554259ff7aSKalyan Thota dspp = mixer[i].hw_dspp; 5564259ff7aSKalyan Thota 5574259ff7aSKalyan Thota if (!dspp || !dspp->ops.setup_pcc) 5584259ff7aSKalyan Thota continue; 5594259ff7aSKalyan Thota 5604259ff7aSKalyan Thota if (!state->ctm) { 5614259ff7aSKalyan Thota dspp->ops.setup_pcc(dspp, NULL); 5624259ff7aSKalyan Thota } else { 5634259ff7aSKalyan Thota _dpu_crtc_get_pcc_coeff(state, &cfg); 5644259ff7aSKalyan Thota dspp->ops.setup_pcc(dspp, &cfg); 5654259ff7aSKalyan Thota } 5664259ff7aSKalyan Thota 5674259ff7aSKalyan Thota mixer[i].flush_mask |= ctl->ops.get_bitmask_dspp(ctl, 5684259ff7aSKalyan Thota mixer[i].hw_dspp->idx); 5694259ff7aSKalyan Thota 5704259ff7aSKalyan Thota /* stage config flush mask */ 5714259ff7aSKalyan Thota ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask); 5724259ff7aSKalyan Thota 5735b702d78SStephen Boyd DRM_DEBUG_ATOMIC("lm %d, ctl %d, flush mask 0x%x\n", 5744259ff7aSKalyan Thota mixer[i].hw_lm->idx - DSPP_0, 5754259ff7aSKalyan Thota ctl->idx - CTL_0, 5764259ff7aSKalyan Thota mixer[i].flush_mask); 5774259ff7aSKalyan Thota } 5784259ff7aSKalyan Thota } 5794259ff7aSKalyan Thota 58025fdd593SJeykumar Sankaran static void dpu_crtc_atomic_begin(struct drm_crtc *crtc, 581f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 58225fdd593SJeykumar Sankaran { 583e12e5263SRob Clark struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 58425fdd593SJeykumar Sankaran struct drm_encoder *encoder; 58525fdd593SJeykumar Sankaran 58625fdd593SJeykumar Sankaran if (!crtc->state->enable) { 5875b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d -> enable %d, skip atomic_begin\n", 58825fdd593SJeykumar Sankaran crtc->base.id, crtc->state->enable); 58925fdd593SJeykumar Sankaran return; 59025fdd593SJeykumar Sankaran } 59125fdd593SJeykumar Sankaran 5925b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d\n", crtc->base.id); 59325fdd593SJeykumar Sankaran 59425fdd593SJeykumar Sankaran _dpu_crtc_setup_lm_bounds(crtc, crtc->state); 59525fdd593SJeykumar Sankaran 59625fdd593SJeykumar Sankaran /* encoder will trigger pending mask now */ 5974b8c6279SSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) 59825fdd593SJeykumar Sankaran dpu_encoder_trigger_kickoff_pending(encoder); 59925fdd593SJeykumar Sankaran 60025fdd593SJeykumar Sankaran /* 60125fdd593SJeykumar Sankaran * If no mixers have been allocated in dpu_crtc_atomic_check(), 60225fdd593SJeykumar Sankaran * it means we are trying to flush a CRTC whose state is disabled: 60325fdd593SJeykumar Sankaran * nothing else needs to be done. 60425fdd593SJeykumar Sankaran */ 6059222cdd2SJeykumar Sankaran if (unlikely(!cstate->num_mixers)) 60625fdd593SJeykumar Sankaran return; 60725fdd593SJeykumar Sankaran 60825fdd593SJeykumar Sankaran _dpu_crtc_blend_setup(crtc); 60925fdd593SJeykumar Sankaran 6104259ff7aSKalyan Thota _dpu_crtc_setup_cp_blocks(crtc); 6114259ff7aSKalyan Thota 61225fdd593SJeykumar Sankaran /* 61325fdd593SJeykumar Sankaran * PP_DONE irq is only used by command mode for now. 61425fdd593SJeykumar Sankaran * It is better to request pending before FLUSH and START trigger 61525fdd593SJeykumar Sankaran * to make sure no pp_done irq missed. 61625fdd593SJeykumar Sankaran * This is safe because no pp_done will happen before SW trigger 61725fdd593SJeykumar Sankaran * in command mode. 61825fdd593SJeykumar Sankaran */ 61925fdd593SJeykumar Sankaran } 62025fdd593SJeykumar Sankaran 62125fdd593SJeykumar Sankaran static void dpu_crtc_atomic_flush(struct drm_crtc *crtc, 622f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 62325fdd593SJeykumar Sankaran { 62425fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc; 62525fdd593SJeykumar Sankaran struct drm_device *dev; 62625fdd593SJeykumar Sankaran struct drm_plane *plane; 62725fdd593SJeykumar Sankaran struct msm_drm_private *priv; 62825fdd593SJeykumar Sankaran unsigned long flags; 62925fdd593SJeykumar Sankaran struct dpu_crtc_state *cstate; 63025fdd593SJeykumar Sankaran 63125fdd593SJeykumar Sankaran if (!crtc->state->enable) { 6325b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d -> enable %d, skip atomic_flush\n", 63325fdd593SJeykumar Sankaran crtc->base.id, crtc->state->enable); 63425fdd593SJeykumar Sankaran return; 63525fdd593SJeykumar Sankaran } 63625fdd593SJeykumar Sankaran 6375b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d\n", crtc->base.id); 63825fdd593SJeykumar Sankaran 63925fdd593SJeykumar Sankaran dpu_crtc = to_dpu_crtc(crtc); 64025fdd593SJeykumar Sankaran cstate = to_dpu_crtc_state(crtc->state); 64125fdd593SJeykumar Sankaran dev = crtc->dev; 64225fdd593SJeykumar Sankaran priv = dev->dev_private; 64325fdd593SJeykumar Sankaran 64425fdd593SJeykumar Sankaran if (crtc->index >= ARRAY_SIZE(priv->event_thread)) { 64525fdd593SJeykumar Sankaran DPU_ERROR("invalid crtc index[%d]\n", crtc->index); 64625fdd593SJeykumar Sankaran return; 64725fdd593SJeykumar Sankaran } 64825fdd593SJeykumar Sankaran 649e12e5263SRob Clark WARN_ON(dpu_crtc->event); 65025fdd593SJeykumar Sankaran spin_lock_irqsave(&dev->event_lock, flags); 65125fdd593SJeykumar Sankaran dpu_crtc->event = crtc->state->event; 65225fdd593SJeykumar Sankaran crtc->state->event = NULL; 65325fdd593SJeykumar Sankaran spin_unlock_irqrestore(&dev->event_lock, flags); 65425fdd593SJeykumar Sankaran 65525fdd593SJeykumar Sankaran /* 65625fdd593SJeykumar Sankaran * If no mixers has been allocated in dpu_crtc_atomic_check(), 65725fdd593SJeykumar Sankaran * it means we are trying to flush a CRTC whose state is disabled: 65825fdd593SJeykumar Sankaran * nothing else needs to be done. 65925fdd593SJeykumar Sankaran */ 6609222cdd2SJeykumar Sankaran if (unlikely(!cstate->num_mixers)) 66125fdd593SJeykumar Sankaran return; 66225fdd593SJeykumar Sankaran 66325fdd593SJeykumar Sankaran /* update performance setting before crtc kickoff */ 66425fdd593SJeykumar Sankaran dpu_core_perf_crtc_update(crtc, 1, false); 66525fdd593SJeykumar Sankaran 66625fdd593SJeykumar Sankaran /* 66725fdd593SJeykumar Sankaran * Final plane updates: Give each plane a chance to complete all 66825fdd593SJeykumar Sankaran * required writes/flushing before crtc's "flush 66925fdd593SJeykumar Sankaran * everything" call below. 67025fdd593SJeykumar Sankaran */ 67125fdd593SJeykumar Sankaran drm_atomic_crtc_for_each_plane(plane, crtc) { 67225fdd593SJeykumar Sankaran if (dpu_crtc->smmu_state.transition_error) 67325fdd593SJeykumar Sankaran dpu_plane_set_error(plane, true); 67425fdd593SJeykumar Sankaran dpu_plane_flush(plane); 67525fdd593SJeykumar Sankaran } 67625fdd593SJeykumar Sankaran 67725fdd593SJeykumar Sankaran /* Kickoff will be scheduled by outer layer */ 67825fdd593SJeykumar Sankaran } 67925fdd593SJeykumar Sankaran 68025fdd593SJeykumar Sankaran /** 68125fdd593SJeykumar Sankaran * dpu_crtc_destroy_state - state destroy hook 68225fdd593SJeykumar Sankaran * @crtc: drm CRTC 68325fdd593SJeykumar Sankaran * @state: CRTC state object to release 68425fdd593SJeykumar Sankaran */ 68525fdd593SJeykumar Sankaran static void dpu_crtc_destroy_state(struct drm_crtc *crtc, 68625fdd593SJeykumar Sankaran struct drm_crtc_state *state) 68725fdd593SJeykumar Sankaran { 688e12e5263SRob Clark struct dpu_crtc_state *cstate = to_dpu_crtc_state(state); 68925fdd593SJeykumar Sankaran 6905b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d\n", crtc->base.id); 69125fdd593SJeykumar Sankaran 69225fdd593SJeykumar Sankaran __drm_atomic_helper_crtc_destroy_state(state); 69325fdd593SJeykumar Sankaran 69425fdd593SJeykumar Sankaran kfree(cstate); 69525fdd593SJeykumar Sankaran } 69625fdd593SJeykumar Sankaran 69725fdd593SJeykumar Sankaran static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) 69825fdd593SJeykumar Sankaran { 69904b96b63SBruce Wang struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 70025fdd593SJeykumar Sankaran int ret, rc = 0; 70125fdd593SJeykumar Sankaran 70225fdd593SJeykumar Sankaran if (!atomic_read(&dpu_crtc->frame_pending)) { 7035b702d78SStephen Boyd DRM_DEBUG_ATOMIC("no frames pending\n"); 70425fdd593SJeykumar Sankaran return 0; 70525fdd593SJeykumar Sankaran } 70625fdd593SJeykumar Sankaran 70725fdd593SJeykumar Sankaran DPU_ATRACE_BEGIN("frame done completion wait"); 70825fdd593SJeykumar Sankaran ret = wait_for_completion_timeout(&dpu_crtc->frame_done_comp, 70970df9610SSean Paul msecs_to_jiffies(DPU_CRTC_FRAME_DONE_TIMEOUT_MS)); 71025fdd593SJeykumar Sankaran if (!ret) { 71125fdd593SJeykumar Sankaran DRM_ERROR("frame done wait timed out, ret:%d\n", ret); 71225fdd593SJeykumar Sankaran rc = -ETIMEDOUT; 71325fdd593SJeykumar Sankaran } 71425fdd593SJeykumar Sankaran DPU_ATRACE_END("frame done completion wait"); 71525fdd593SJeykumar Sankaran 71625fdd593SJeykumar Sankaran return rc; 71725fdd593SJeykumar Sankaran } 71825fdd593SJeykumar Sankaran 719b4bb9f15SRob Clark void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) 72025fdd593SJeykumar Sankaran { 72125fdd593SJeykumar Sankaran struct drm_encoder *encoder; 72204b96b63SBruce Wang struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 72304b96b63SBruce Wang struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); 72404b96b63SBruce Wang struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 72525fdd593SJeykumar Sankaran 72625fdd593SJeykumar Sankaran /* 72725fdd593SJeykumar Sankaran * If no mixers has been allocated in dpu_crtc_atomic_check(), 72825fdd593SJeykumar Sankaran * it means we are trying to start a CRTC whose state is disabled: 72925fdd593SJeykumar Sankaran * nothing else needs to be done. 73025fdd593SJeykumar Sankaran */ 7319222cdd2SJeykumar Sankaran if (unlikely(!cstate->num_mixers)) 73225fdd593SJeykumar Sankaran return; 73325fdd593SJeykumar Sankaran 73425fdd593SJeykumar Sankaran DPU_ATRACE_BEGIN("crtc_commit"); 73525fdd593SJeykumar Sankaran 73625fdd593SJeykumar Sankaran /* 7374b8c6279SSean Paul * Encoder will flush/start now, unless it has a tx pending. If so, it 7384b8c6279SSean Paul * may delay and flush at an irq event (e.g. ppdone) 73925fdd593SJeykumar Sankaran */ 7404b8c6279SSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, 741d3db61caSBruce Wang crtc->state->encoder_mask) 7420c91ed51SRob Clark dpu_encoder_prepare_for_kickoff(encoder); 74350bcc689SSean Paul 74425fdd593SJeykumar Sankaran if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) { 74525fdd593SJeykumar Sankaran /* acquire bandwidth and other resources */ 7465b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d first commit\n", crtc->base.id); 74725fdd593SJeykumar Sankaran } else 7485b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d commit\n", crtc->base.id); 74925fdd593SJeykumar Sankaran 75025fdd593SJeykumar Sankaran dpu_crtc->play_count++; 75125fdd593SJeykumar Sankaran 75225fdd593SJeykumar Sankaran dpu_vbif_clear_errors(dpu_kms); 75325fdd593SJeykumar Sankaran 7544b8c6279SSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) 755b4bb9f15SRob Clark dpu_encoder_kickoff(encoder); 75625fdd593SJeykumar Sankaran 75725fdd593SJeykumar Sankaran reinit_completion(&dpu_crtc->frame_done_comp); 75825fdd593SJeykumar Sankaran DPU_ATRACE_END("crtc_commit"); 75925fdd593SJeykumar Sankaran } 76025fdd593SJeykumar Sankaran 761ff5952a7SSean Paul static void dpu_crtc_reset(struct drm_crtc *crtc) 76225fdd593SJeykumar Sankaran { 7631cff7440SMaarten Lankhorst struct dpu_crtc_state *cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); 76425fdd593SJeykumar Sankaran 765ff5952a7SSean Paul if (crtc->state) 766ff5952a7SSean Paul dpu_crtc_destroy_state(crtc, crtc->state); 76725fdd593SJeykumar Sankaran 7681cff7440SMaarten Lankhorst __drm_atomic_helper_crtc_reset(crtc, &cstate->base); 76925fdd593SJeykumar Sankaran } 77025fdd593SJeykumar Sankaran 77125fdd593SJeykumar Sankaran /** 77225fdd593SJeykumar Sankaran * dpu_crtc_duplicate_state - state duplicate hook 77325fdd593SJeykumar Sankaran * @crtc: Pointer to drm crtc structure 77425fdd593SJeykumar Sankaran */ 77525fdd593SJeykumar Sankaran static struct drm_crtc_state *dpu_crtc_duplicate_state(struct drm_crtc *crtc) 77625fdd593SJeykumar Sankaran { 777e12e5263SRob Clark struct dpu_crtc_state *cstate, *old_cstate = to_dpu_crtc_state(crtc->state); 77825fdd593SJeykumar Sankaran 77925fdd593SJeykumar Sankaran cstate = kmemdup(old_cstate, sizeof(*old_cstate), GFP_KERNEL); 78025fdd593SJeykumar Sankaran if (!cstate) { 78125fdd593SJeykumar Sankaran DPU_ERROR("failed to allocate state\n"); 78225fdd593SJeykumar Sankaran return NULL; 78325fdd593SJeykumar Sankaran } 78425fdd593SJeykumar Sankaran 78525fdd593SJeykumar Sankaran /* duplicate base helper */ 78625fdd593SJeykumar Sankaran __drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base); 78725fdd593SJeykumar Sankaran 78825fdd593SJeykumar Sankaran return &cstate->base; 78925fdd593SJeykumar Sankaran } 79025fdd593SJeykumar Sankaran 791f7aafc8dSSean Paul static void dpu_crtc_disable(struct drm_crtc *crtc, 792351f950dSMaxime Ripard struct drm_atomic_state *state) 79325fdd593SJeykumar Sankaran { 794351f950dSMaxime Ripard struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 795351f950dSMaxime Ripard crtc); 796e12e5263SRob Clark struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 797e12e5263SRob Clark struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state); 79825fdd593SJeykumar Sankaran struct drm_encoder *encoder; 7992f2eb723SRajesh Yadav unsigned long flags; 800241b507cSRob Clark bool release_bandwidth = false; 80125fdd593SJeykumar Sankaran 80225fdd593SJeykumar Sankaran DRM_DEBUG_KMS("crtc%d\n", crtc->base.id); 80325fdd593SJeykumar Sankaran 8042f2eb723SRajesh Yadav /* Disable/save vblank irq handling */ 8052f2eb723SRajesh Yadav drm_crtc_vblank_off(crtc); 8062f2eb723SRajesh Yadav 807a796ba2cSSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, 808241b507cSRob Clark old_crtc_state->encoder_mask) { 809241b507cSRob Clark /* in video mode, we hold an extra bandwidth reference 810241b507cSRob Clark * as we cannot drop bandwidth at frame-done if any 811241b507cSRob Clark * crtc is being used in video mode. 812241b507cSRob Clark */ 813241b507cSRob Clark if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) 814241b507cSRob Clark release_bandwidth = true; 815a796ba2cSSean Paul dpu_encoder_assign_crtc(encoder, NULL); 816241b507cSRob Clark } 81725fdd593SJeykumar Sankaran 81825fdd593SJeykumar Sankaran /* wait for frame_event_done completion */ 81925fdd593SJeykumar Sankaran if (_dpu_crtc_wait_for_frame_done(crtc)) 82025fdd593SJeykumar Sankaran DPU_ERROR("crtc%d wait for frame done failed;frame_pending%d\n", 82125fdd593SJeykumar Sankaran crtc->base.id, 82225fdd593SJeykumar Sankaran atomic_read(&dpu_crtc->frame_pending)); 82325fdd593SJeykumar Sankaran 82425fdd593SJeykumar Sankaran trace_dpu_crtc_disable(DRMID(crtc), false, dpu_crtc); 82525fdd593SJeykumar Sankaran dpu_crtc->enabled = false; 82625fdd593SJeykumar Sankaran 82725fdd593SJeykumar Sankaran if (atomic_read(&dpu_crtc->frame_pending)) { 82825fdd593SJeykumar Sankaran trace_dpu_crtc_disable_frame_pending(DRMID(crtc), 82925fdd593SJeykumar Sankaran atomic_read(&dpu_crtc->frame_pending)); 830241b507cSRob Clark if (release_bandwidth) 83125fdd593SJeykumar Sankaran dpu_core_perf_crtc_release_bw(crtc); 83225fdd593SJeykumar Sankaran atomic_set(&dpu_crtc->frame_pending, 0); 83325fdd593SJeykumar Sankaran } 83425fdd593SJeykumar Sankaran 83525fdd593SJeykumar Sankaran dpu_core_perf_crtc_update(crtc, 0, true); 83625fdd593SJeykumar Sankaran 8374b8c6279SSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) 83825fdd593SJeykumar Sankaran dpu_encoder_register_frame_event_callback(encoder, NULL, NULL); 83925fdd593SJeykumar Sankaran 8409222cdd2SJeykumar Sankaran memset(cstate->mixers, 0, sizeof(cstate->mixers)); 8419222cdd2SJeykumar Sankaran cstate->num_mixers = 0; 84225fdd593SJeykumar Sankaran 84325fdd593SJeykumar Sankaran /* disable clk & bw control until clk & bw properties are set */ 84425fdd593SJeykumar Sankaran cstate->bw_control = false; 84525fdd593SJeykumar Sankaran cstate->bw_split_vote = false; 84625fdd593SJeykumar Sankaran 8472f2eb723SRajesh Yadav if (crtc->state->event && !crtc->state->active) { 8482f2eb723SRajesh Yadav spin_lock_irqsave(&crtc->dev->event_lock, flags); 8492f2eb723SRajesh Yadav drm_crtc_send_vblank_event(crtc, crtc->state->event); 8502f2eb723SRajesh Yadav crtc->state->event = NULL; 8512f2eb723SRajesh Yadav spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 8522f2eb723SRajesh Yadav } 853b77d0f0dSSean Paul 854b77d0f0dSSean Paul pm_runtime_put_sync(crtc->dev->dev); 85525fdd593SJeykumar Sankaran } 85625fdd593SJeykumar Sankaran 85725fdd593SJeykumar Sankaran static void dpu_crtc_enable(struct drm_crtc *crtc, 858351f950dSMaxime Ripard struct drm_atomic_state *state) 85925fdd593SJeykumar Sankaran { 860e12e5263SRob Clark struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 86125fdd593SJeykumar Sankaran struct drm_encoder *encoder; 86235c719daSRob Clark bool request_bandwidth = false; 86325fdd593SJeykumar Sankaran 864b77d0f0dSSean Paul pm_runtime_get_sync(crtc->dev->dev); 865b77d0f0dSSean Paul 86625fdd593SJeykumar Sankaran DRM_DEBUG_KMS("crtc%d\n", crtc->base.id); 86725fdd593SJeykumar Sankaran 868241b507cSRob Clark drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { 869241b507cSRob Clark /* in video mode, we hold an extra bandwidth reference 870241b507cSRob Clark * as we cannot drop bandwidth at frame-done if any 871241b507cSRob Clark * crtc is being used in video mode. 872241b507cSRob Clark */ 873241b507cSRob Clark if (dpu_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) 874241b507cSRob Clark request_bandwidth = true; 87525fdd593SJeykumar Sankaran dpu_encoder_register_frame_event_callback(encoder, 87625fdd593SJeykumar Sankaran dpu_crtc_frame_event_cb, (void *)crtc); 877241b507cSRob Clark } 878241b507cSRob Clark 879241b507cSRob Clark if (request_bandwidth) 880241b507cSRob Clark atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref); 88125fdd593SJeykumar Sankaran 88225fdd593SJeykumar Sankaran trace_dpu_crtc_enable(DRMID(crtc), true, dpu_crtc); 88325fdd593SJeykumar Sankaran dpu_crtc->enabled = true; 88425fdd593SJeykumar Sankaran 885a796ba2cSSean Paul drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) 886a796ba2cSSean Paul dpu_encoder_assign_crtc(encoder, crtc); 88725fdd593SJeykumar Sankaran 8882f2eb723SRajesh Yadav /* Enable/restore vblank irq handling */ 8892f2eb723SRajesh Yadav drm_crtc_vblank_on(crtc); 89025fdd593SJeykumar Sankaran } 89125fdd593SJeykumar Sankaran 89225fdd593SJeykumar Sankaran struct plane_state { 89325fdd593SJeykumar Sankaran struct dpu_plane_state *dpu_pstate; 89425fdd593SJeykumar Sankaran const struct drm_plane_state *drm_pstate; 89525fdd593SJeykumar Sankaran int stage; 89625fdd593SJeykumar Sankaran u32 pipe_id; 89725fdd593SJeykumar Sankaran }; 89825fdd593SJeykumar Sankaran 89925fdd593SJeykumar Sankaran static int dpu_crtc_atomic_check(struct drm_crtc *crtc, 90029b77ad7SMaxime Ripard struct drm_atomic_state *state) 90125fdd593SJeykumar Sankaran { 90229b77ad7SMaxime Ripard struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 90329b77ad7SMaxime Ripard crtc); 904e12e5263SRob Clark struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 90529b77ad7SMaxime Ripard struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc_state); 90625fdd593SJeykumar Sankaran struct plane_state *pstates; 90725fdd593SJeykumar Sankaran 90825fdd593SJeykumar Sankaran const struct drm_plane_state *pstate; 90925fdd593SJeykumar Sankaran struct drm_plane *plane; 91025fdd593SJeykumar Sankaran struct drm_display_mode *mode; 91125fdd593SJeykumar Sankaran 91222f76094SStephen Boyd int cnt = 0, rc = 0, mixer_width = 0, i, z_pos; 91325fdd593SJeykumar Sankaran 91425fdd593SJeykumar Sankaran struct dpu_multirect_plane_states multirect_plane[DPU_STAGE_MAX * 2]; 91525fdd593SJeykumar Sankaran int multirect_count = 0; 91625fdd593SJeykumar Sankaran const struct drm_plane_state *pipe_staged[SSPP_MAX]; 91725fdd593SJeykumar Sankaran int left_zpos_cnt = 0, right_zpos_cnt = 0; 91825fdd593SJeykumar Sankaran struct drm_rect crtc_rect = { 0 }; 91925fdd593SJeykumar Sankaran 92025fdd593SJeykumar Sankaran pstates = kzalloc(sizeof(*pstates) * DPU_STAGE_MAX * 4, GFP_KERNEL); 92125fdd593SJeykumar Sankaran 92229b77ad7SMaxime Ripard if (!crtc_state->enable || !crtc_state->active) { 9235b702d78SStephen Boyd DRM_DEBUG_ATOMIC("crtc%d -> enable %d, active %d, skip atomic_check\n", 92429b77ad7SMaxime Ripard crtc->base.id, crtc_state->enable, 92529b77ad7SMaxime Ripard crtc_state->active); 926a29c8c02SKalyan Thota memset(&cstate->new_perf, 0, sizeof(cstate->new_perf)); 92725fdd593SJeykumar Sankaran goto end; 92825fdd593SJeykumar Sankaran } 92925fdd593SJeykumar Sankaran 93029b77ad7SMaxime Ripard mode = &crtc_state->adjusted_mode; 9315b702d78SStephen Boyd DRM_DEBUG_ATOMIC("%s: check\n", dpu_crtc->name); 93225fdd593SJeykumar Sankaran 93325fdd593SJeykumar Sankaran /* force a full mode set if active state changed */ 93429b77ad7SMaxime Ripard if (crtc_state->active_changed) 93529b77ad7SMaxime Ripard crtc_state->mode_changed = true; 93625fdd593SJeykumar Sankaran 93725fdd593SJeykumar Sankaran memset(pipe_staged, 0, sizeof(pipe_staged)); 93825fdd593SJeykumar Sankaran 93922f76094SStephen Boyd if (cstate->num_mixers) { 9403804a982SJordan Crouse mixer_width = mode->hdisplay / cstate->num_mixers; 94125fdd593SJeykumar Sankaran 94229b77ad7SMaxime Ripard _dpu_crtc_setup_lm_bounds(crtc, crtc_state); 94322f76094SStephen Boyd } 94425fdd593SJeykumar Sankaran 94525fdd593SJeykumar Sankaran crtc_rect.x2 = mode->hdisplay; 94625fdd593SJeykumar Sankaran crtc_rect.y2 = mode->vdisplay; 94725fdd593SJeykumar Sankaran 94825fdd593SJeykumar Sankaran /* get plane state for all drm planes associated with crtc state */ 94929b77ad7SMaxime Ripard drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) { 95025fdd593SJeykumar Sankaran struct drm_rect dst, clip = crtc_rect; 95125fdd593SJeykumar Sankaran 95225fdd593SJeykumar Sankaran if (IS_ERR_OR_NULL(pstate)) { 95325fdd593SJeykumar Sankaran rc = PTR_ERR(pstate); 95425fdd593SJeykumar Sankaran DPU_ERROR("%s: failed to get plane%d state, %d\n", 95525fdd593SJeykumar Sankaran dpu_crtc->name, plane->base.id, rc); 95625fdd593SJeykumar Sankaran goto end; 95725fdd593SJeykumar Sankaran } 95825fdd593SJeykumar Sankaran if (cnt >= DPU_STAGE_MAX * 4) 95925fdd593SJeykumar Sankaran continue; 96025fdd593SJeykumar Sankaran 96125fdd593SJeykumar Sankaran pstates[cnt].dpu_pstate = to_dpu_plane_state(pstate); 96225fdd593SJeykumar Sankaran pstates[cnt].drm_pstate = pstate; 96325fdd593SJeykumar Sankaran pstates[cnt].stage = pstate->normalized_zpos; 96425fdd593SJeykumar Sankaran pstates[cnt].pipe_id = dpu_plane_pipe(plane); 96525fdd593SJeykumar Sankaran 96625fdd593SJeykumar Sankaran if (pipe_staged[pstates[cnt].pipe_id]) { 96725fdd593SJeykumar Sankaran multirect_plane[multirect_count].r0 = 96825fdd593SJeykumar Sankaran pipe_staged[pstates[cnt].pipe_id]; 96925fdd593SJeykumar Sankaran multirect_plane[multirect_count].r1 = pstate; 97025fdd593SJeykumar Sankaran multirect_count++; 97125fdd593SJeykumar Sankaran 97225fdd593SJeykumar Sankaran pipe_staged[pstates[cnt].pipe_id] = NULL; 97325fdd593SJeykumar Sankaran } else { 97425fdd593SJeykumar Sankaran pipe_staged[pstates[cnt].pipe_id] = pstate; 97525fdd593SJeykumar Sankaran } 97625fdd593SJeykumar Sankaran 97725fdd593SJeykumar Sankaran cnt++; 97825fdd593SJeykumar Sankaran 97925fdd593SJeykumar Sankaran dst = drm_plane_state_dest(pstate); 98096fc56a7SSean Paul if (!drm_rect_intersect(&clip, &dst)) { 98125fdd593SJeykumar Sankaran DPU_ERROR("invalid vertical/horizontal destination\n"); 98225fdd593SJeykumar Sankaran DPU_ERROR("display: " DRM_RECT_FMT " plane: " 98325fdd593SJeykumar Sankaran DRM_RECT_FMT "\n", DRM_RECT_ARG(&crtc_rect), 98425fdd593SJeykumar Sankaran DRM_RECT_ARG(&dst)); 98525fdd593SJeykumar Sankaran rc = -E2BIG; 98625fdd593SJeykumar Sankaran goto end; 98725fdd593SJeykumar Sankaran } 98825fdd593SJeykumar Sankaran } 98925fdd593SJeykumar Sankaran 99025fdd593SJeykumar Sankaran for (i = 1; i < SSPP_MAX; i++) { 99125fdd593SJeykumar Sankaran if (pipe_staged[i]) { 99225fdd593SJeykumar Sankaran dpu_plane_clear_multirect(pipe_staged[i]); 99325fdd593SJeykumar Sankaran 99425fdd593SJeykumar Sankaran if (is_dpu_plane_virtual(pipe_staged[i]->plane)) { 99525fdd593SJeykumar Sankaran DPU_ERROR( 99625fdd593SJeykumar Sankaran "r1 only virt plane:%d not supported\n", 99725fdd593SJeykumar Sankaran pipe_staged[i]->plane->base.id); 99825fdd593SJeykumar Sankaran rc = -EINVAL; 99925fdd593SJeykumar Sankaran goto end; 100025fdd593SJeykumar Sankaran } 100125fdd593SJeykumar Sankaran } 100225fdd593SJeykumar Sankaran } 100325fdd593SJeykumar Sankaran 100425fdd593SJeykumar Sankaran z_pos = -1; 100525fdd593SJeykumar Sankaran for (i = 0; i < cnt; i++) { 100625fdd593SJeykumar Sankaran /* reset counts at every new blend stage */ 100725fdd593SJeykumar Sankaran if (pstates[i].stage != z_pos) { 100825fdd593SJeykumar Sankaran left_zpos_cnt = 0; 100925fdd593SJeykumar Sankaran right_zpos_cnt = 0; 101025fdd593SJeykumar Sankaran z_pos = pstates[i].stage; 101125fdd593SJeykumar Sankaran } 101225fdd593SJeykumar Sankaran 101325fdd593SJeykumar Sankaran /* verify z_pos setting before using it */ 101425fdd593SJeykumar Sankaran if (z_pos >= DPU_STAGE_MAX - DPU_STAGE_0) { 101525fdd593SJeykumar Sankaran DPU_ERROR("> %d plane stages assigned\n", 101625fdd593SJeykumar Sankaran DPU_STAGE_MAX - DPU_STAGE_0); 101725fdd593SJeykumar Sankaran rc = -EINVAL; 101825fdd593SJeykumar Sankaran goto end; 101925fdd593SJeykumar Sankaran } else if (pstates[i].drm_pstate->crtc_x < mixer_width) { 102025fdd593SJeykumar Sankaran if (left_zpos_cnt == 2) { 102125fdd593SJeykumar Sankaran DPU_ERROR("> 2 planes @ stage %d on left\n", 102225fdd593SJeykumar Sankaran z_pos); 102325fdd593SJeykumar Sankaran rc = -EINVAL; 102425fdd593SJeykumar Sankaran goto end; 102525fdd593SJeykumar Sankaran } 102625fdd593SJeykumar Sankaran left_zpos_cnt++; 102725fdd593SJeykumar Sankaran 102825fdd593SJeykumar Sankaran } else { 102925fdd593SJeykumar Sankaran if (right_zpos_cnt == 2) { 103025fdd593SJeykumar Sankaran DPU_ERROR("> 2 planes @ stage %d on right\n", 103125fdd593SJeykumar Sankaran z_pos); 103225fdd593SJeykumar Sankaran rc = -EINVAL; 103325fdd593SJeykumar Sankaran goto end; 103425fdd593SJeykumar Sankaran } 103525fdd593SJeykumar Sankaran right_zpos_cnt++; 103625fdd593SJeykumar Sankaran } 103725fdd593SJeykumar Sankaran 103825fdd593SJeykumar Sankaran pstates[i].dpu_pstate->stage = z_pos + DPU_STAGE_0; 10395b702d78SStephen Boyd DRM_DEBUG_ATOMIC("%s: zpos %d\n", dpu_crtc->name, z_pos); 104025fdd593SJeykumar Sankaran } 104125fdd593SJeykumar Sankaran 104225fdd593SJeykumar Sankaran for (i = 0; i < multirect_count; i++) { 104325fdd593SJeykumar Sankaran if (dpu_plane_validate_multirect_v2(&multirect_plane[i])) { 104425fdd593SJeykumar Sankaran DPU_ERROR( 104525fdd593SJeykumar Sankaran "multirect validation failed for planes (%d - %d)\n", 104625fdd593SJeykumar Sankaran multirect_plane[i].r0->plane->base.id, 104725fdd593SJeykumar Sankaran multirect_plane[i].r1->plane->base.id); 104825fdd593SJeykumar Sankaran rc = -EINVAL; 104925fdd593SJeykumar Sankaran goto end; 105025fdd593SJeykumar Sankaran } 105125fdd593SJeykumar Sankaran } 105225fdd593SJeykumar Sankaran 1053241b507cSRob Clark atomic_inc(&_dpu_crtc_get_kms(crtc)->bandwidth_ref); 1054241b507cSRob Clark 105529b77ad7SMaxime Ripard rc = dpu_core_perf_crtc_check(crtc, crtc_state); 105625fdd593SJeykumar Sankaran if (rc) { 105725fdd593SJeykumar Sankaran DPU_ERROR("crtc%d failed performance check %d\n", 105825fdd593SJeykumar Sankaran crtc->base.id, rc); 105925fdd593SJeykumar Sankaran goto end; 106025fdd593SJeykumar Sankaran } 106125fdd593SJeykumar Sankaran 106225fdd593SJeykumar Sankaran /* validate source split: 106325fdd593SJeykumar Sankaran * use pstates sorted by stage to check planes on same stage 106425fdd593SJeykumar Sankaran * we assume that all pipes are in source split so its valid to compare 106525fdd593SJeykumar Sankaran * without taking into account left/right mixer placement 106625fdd593SJeykumar Sankaran */ 106725fdd593SJeykumar Sankaran for (i = 1; i < cnt; i++) { 106825fdd593SJeykumar Sankaran struct plane_state *prv_pstate, *cur_pstate; 106925fdd593SJeykumar Sankaran struct drm_rect left_rect, right_rect; 107025fdd593SJeykumar Sankaran int32_t left_pid, right_pid; 107125fdd593SJeykumar Sankaran int32_t stage; 107225fdd593SJeykumar Sankaran 107325fdd593SJeykumar Sankaran prv_pstate = &pstates[i - 1]; 107425fdd593SJeykumar Sankaran cur_pstate = &pstates[i]; 107525fdd593SJeykumar Sankaran if (prv_pstate->stage != cur_pstate->stage) 107625fdd593SJeykumar Sankaran continue; 107725fdd593SJeykumar Sankaran 107825fdd593SJeykumar Sankaran stage = cur_pstate->stage; 107925fdd593SJeykumar Sankaran 108025fdd593SJeykumar Sankaran left_pid = prv_pstate->dpu_pstate->base.plane->base.id; 108125fdd593SJeykumar Sankaran left_rect = drm_plane_state_dest(prv_pstate->drm_pstate); 108225fdd593SJeykumar Sankaran 108325fdd593SJeykumar Sankaran right_pid = cur_pstate->dpu_pstate->base.plane->base.id; 108425fdd593SJeykumar Sankaran right_rect = drm_plane_state_dest(cur_pstate->drm_pstate); 108525fdd593SJeykumar Sankaran 108625fdd593SJeykumar Sankaran if (right_rect.x1 < left_rect.x1) { 108725fdd593SJeykumar Sankaran swap(left_pid, right_pid); 108825fdd593SJeykumar Sankaran swap(left_rect, right_rect); 108925fdd593SJeykumar Sankaran } 109025fdd593SJeykumar Sankaran 109125fdd593SJeykumar Sankaran /** 109225fdd593SJeykumar Sankaran * - planes are enumerated in pipe-priority order such that 109325fdd593SJeykumar Sankaran * planes with lower drm_id must be left-most in a shared 109425fdd593SJeykumar Sankaran * blend-stage when using source split. 109525fdd593SJeykumar Sankaran * - planes in source split must be contiguous in width 109625fdd593SJeykumar Sankaran * - planes in source split must have same dest yoff and height 109725fdd593SJeykumar Sankaran */ 109825fdd593SJeykumar Sankaran if (right_pid < left_pid) { 109925fdd593SJeykumar Sankaran DPU_ERROR( 110025fdd593SJeykumar Sankaran "invalid src split cfg. priority mismatch. stage: %d left: %d right: %d\n", 110125fdd593SJeykumar Sankaran stage, left_pid, right_pid); 110225fdd593SJeykumar Sankaran rc = -EINVAL; 110325fdd593SJeykumar Sankaran goto end; 110425fdd593SJeykumar Sankaran } else if (right_rect.x1 != drm_rect_width(&left_rect)) { 110525fdd593SJeykumar Sankaran DPU_ERROR("non-contiguous coordinates for src split. " 110625fdd593SJeykumar Sankaran "stage: %d left: " DRM_RECT_FMT " right: " 110725fdd593SJeykumar Sankaran DRM_RECT_FMT "\n", stage, 110825fdd593SJeykumar Sankaran DRM_RECT_ARG(&left_rect), 110925fdd593SJeykumar Sankaran DRM_RECT_ARG(&right_rect)); 111025fdd593SJeykumar Sankaran rc = -EINVAL; 111125fdd593SJeykumar Sankaran goto end; 111225fdd593SJeykumar Sankaran } else if (left_rect.y1 != right_rect.y1 || 111325fdd593SJeykumar Sankaran drm_rect_height(&left_rect) != drm_rect_height(&right_rect)) { 111425fdd593SJeykumar Sankaran DPU_ERROR("source split at stage: %d. invalid " 111525fdd593SJeykumar Sankaran "yoff/height: left: " DRM_RECT_FMT " right: " 111625fdd593SJeykumar Sankaran DRM_RECT_FMT "\n", stage, 111725fdd593SJeykumar Sankaran DRM_RECT_ARG(&left_rect), 111825fdd593SJeykumar Sankaran DRM_RECT_ARG(&right_rect)); 111925fdd593SJeykumar Sankaran rc = -EINVAL; 112025fdd593SJeykumar Sankaran goto end; 112125fdd593SJeykumar Sankaran } 112225fdd593SJeykumar Sankaran } 112325fdd593SJeykumar Sankaran 112425fdd593SJeykumar Sankaran end: 112525fdd593SJeykumar Sankaran kfree(pstates); 112625fdd593SJeykumar Sankaran return rc; 112725fdd593SJeykumar Sankaran } 112825fdd593SJeykumar Sankaran 112925fdd593SJeykumar Sankaran int dpu_crtc_vblank(struct drm_crtc *crtc, bool en) 113025fdd593SJeykumar Sankaran { 11317a007a12SBruce Wang struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 1132a796ba2cSSean Paul struct drm_encoder *enc; 113325fdd593SJeykumar Sankaran 113425fdd593SJeykumar Sankaran trace_dpu_crtc_vblank(DRMID(&dpu_crtc->base), en, dpu_crtc); 1135a796ba2cSSean Paul 1136a796ba2cSSean Paul /* 1137a796ba2cSSean Paul * Normally we would iterate through encoder_mask in crtc state to find 1138a796ba2cSSean Paul * attached encoders. In this case, we might be disabling vblank _after_ 1139a796ba2cSSean Paul * encoder_mask has been cleared. 1140a796ba2cSSean Paul * 1141a796ba2cSSean Paul * Instead, we "assign" a crtc to the encoder in enable and clear it in 1142a796ba2cSSean Paul * disable (which is also after encoder_mask is cleared). So instead of 1143a796ba2cSSean Paul * using encoder mask, we'll ask the encoder to toggle itself iff it's 1144a796ba2cSSean Paul * currently assigned to our crtc. 1145a796ba2cSSean Paul * 1146a796ba2cSSean Paul * Note also that this function cannot be called while crtc is disabled 1147a796ba2cSSean Paul * since we use drm_crtc_vblank_on/off. So we don't need to worry 1148a796ba2cSSean Paul * about the assigned crtcs being inconsistent with the current state 1149a796ba2cSSean Paul * (which means no need to worry about modeset locks). 1150a796ba2cSSean Paul */ 1151a796ba2cSSean Paul list_for_each_entry(enc, &crtc->dev->mode_config.encoder_list, head) { 1152a796ba2cSSean Paul trace_dpu_crtc_vblank_enable(DRMID(crtc), DRMID(enc), en, 1153a796ba2cSSean Paul dpu_crtc); 1154a796ba2cSSean Paul 1155a796ba2cSSean Paul dpu_encoder_toggle_vblank_for_crtc(enc, crtc, en); 115625fdd593SJeykumar Sankaran } 115725fdd593SJeykumar Sankaran 115825fdd593SJeykumar Sankaran return 0; 115925fdd593SJeykumar Sankaran } 116025fdd593SJeykumar Sankaran 116125fdd593SJeykumar Sankaran #ifdef CONFIG_DEBUG_FS 116225fdd593SJeykumar Sankaran static int _dpu_debugfs_status_show(struct seq_file *s, void *data) 116325fdd593SJeykumar Sankaran { 116425fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc; 116525fdd593SJeykumar Sankaran struct dpu_plane_state *pstate = NULL; 116625fdd593SJeykumar Sankaran struct dpu_crtc_mixer *m; 116725fdd593SJeykumar Sankaran 116825fdd593SJeykumar Sankaran struct drm_crtc *crtc; 116925fdd593SJeykumar Sankaran struct drm_plane *plane; 117025fdd593SJeykumar Sankaran struct drm_display_mode *mode; 117125fdd593SJeykumar Sankaran struct drm_framebuffer *fb; 117225fdd593SJeykumar Sankaran struct drm_plane_state *state; 117325fdd593SJeykumar Sankaran struct dpu_crtc_state *cstate; 117425fdd593SJeykumar Sankaran 117525fdd593SJeykumar Sankaran int i, out_width; 117625fdd593SJeykumar Sankaran 117725fdd593SJeykumar Sankaran dpu_crtc = s->private; 117825fdd593SJeykumar Sankaran crtc = &dpu_crtc->base; 11799222cdd2SJeykumar Sankaran 11809222cdd2SJeykumar Sankaran drm_modeset_lock_all(crtc->dev); 118125fdd593SJeykumar Sankaran cstate = to_dpu_crtc_state(crtc->state); 118225fdd593SJeykumar Sankaran 118325fdd593SJeykumar Sankaran mode = &crtc->state->adjusted_mode; 11843804a982SJordan Crouse out_width = mode->hdisplay / cstate->num_mixers; 118525fdd593SJeykumar Sankaran 118625fdd593SJeykumar Sankaran seq_printf(s, "crtc:%d width:%d height:%d\n", crtc->base.id, 118725fdd593SJeykumar Sankaran mode->hdisplay, mode->vdisplay); 118825fdd593SJeykumar Sankaran 118925fdd593SJeykumar Sankaran seq_puts(s, "\n"); 119025fdd593SJeykumar Sankaran 11919222cdd2SJeykumar Sankaran for (i = 0; i < cstate->num_mixers; ++i) { 11929222cdd2SJeykumar Sankaran m = &cstate->mixers[i]; 119325fdd593SJeykumar Sankaran seq_printf(s, "\tmixer:%d ctl:%d width:%d height:%d\n", 1194cf6916f4SJeykumar Sankaran m->hw_lm->idx - LM_0, m->lm_ctl->idx - CTL_0, 119525fdd593SJeykumar Sankaran out_width, mode->vdisplay); 119625fdd593SJeykumar Sankaran } 119725fdd593SJeykumar Sankaran 119825fdd593SJeykumar Sankaran seq_puts(s, "\n"); 119925fdd593SJeykumar Sankaran 120025fdd593SJeykumar Sankaran drm_atomic_crtc_for_each_plane(plane, crtc) { 120125fdd593SJeykumar Sankaran pstate = to_dpu_plane_state(plane->state); 120225fdd593SJeykumar Sankaran state = plane->state; 120325fdd593SJeykumar Sankaran 120425fdd593SJeykumar Sankaran if (!pstate || !state) 120525fdd593SJeykumar Sankaran continue; 120625fdd593SJeykumar Sankaran 120725fdd593SJeykumar Sankaran seq_printf(s, "\tplane:%u stage:%d\n", plane->base.id, 120825fdd593SJeykumar Sankaran pstate->stage); 120925fdd593SJeykumar Sankaran 121025fdd593SJeykumar Sankaran if (plane->state->fb) { 121125fdd593SJeykumar Sankaran fb = plane->state->fb; 121225fdd593SJeykumar Sankaran 121325fdd593SJeykumar Sankaran seq_printf(s, "\tfb:%d image format:%4.4s wxh:%ux%u ", 121425fdd593SJeykumar Sankaran fb->base.id, (char *) &fb->format->format, 121525fdd593SJeykumar Sankaran fb->width, fb->height); 121625fdd593SJeykumar Sankaran for (i = 0; i < ARRAY_SIZE(fb->format->cpp); ++i) 121725fdd593SJeykumar Sankaran seq_printf(s, "cpp[%d]:%u ", 121825fdd593SJeykumar Sankaran i, fb->format->cpp[i]); 121925fdd593SJeykumar Sankaran seq_puts(s, "\n\t"); 122025fdd593SJeykumar Sankaran 122125fdd593SJeykumar Sankaran seq_printf(s, "modifier:%8llu ", fb->modifier); 122225fdd593SJeykumar Sankaran seq_puts(s, "\n"); 122325fdd593SJeykumar Sankaran 122425fdd593SJeykumar Sankaran seq_puts(s, "\t"); 122525fdd593SJeykumar Sankaran for (i = 0; i < ARRAY_SIZE(fb->pitches); i++) 122625fdd593SJeykumar Sankaran seq_printf(s, "pitches[%d]:%8u ", i, 122725fdd593SJeykumar Sankaran fb->pitches[i]); 122825fdd593SJeykumar Sankaran seq_puts(s, "\n"); 122925fdd593SJeykumar Sankaran 123025fdd593SJeykumar Sankaran seq_puts(s, "\t"); 123125fdd593SJeykumar Sankaran for (i = 0; i < ARRAY_SIZE(fb->offsets); i++) 123225fdd593SJeykumar Sankaran seq_printf(s, "offsets[%d]:%8u ", i, 123325fdd593SJeykumar Sankaran fb->offsets[i]); 123425fdd593SJeykumar Sankaran seq_puts(s, "\n"); 123525fdd593SJeykumar Sankaran } 123625fdd593SJeykumar Sankaran 123725fdd593SJeykumar Sankaran seq_printf(s, "\tsrc_x:%4d src_y:%4d src_w:%4d src_h:%4d\n", 123825fdd593SJeykumar Sankaran state->src_x, state->src_y, state->src_w, state->src_h); 123925fdd593SJeykumar Sankaran 124025fdd593SJeykumar Sankaran seq_printf(s, "\tdst x:%4d dst_y:%4d dst_w:%4d dst_h:%4d\n", 124125fdd593SJeykumar Sankaran state->crtc_x, state->crtc_y, state->crtc_w, 124225fdd593SJeykumar Sankaran state->crtc_h); 124325fdd593SJeykumar Sankaran seq_printf(s, "\tmultirect: mode: %d index: %d\n", 124425fdd593SJeykumar Sankaran pstate->multirect_mode, pstate->multirect_index); 124525fdd593SJeykumar Sankaran 124625fdd593SJeykumar Sankaran seq_puts(s, "\n"); 124725fdd593SJeykumar Sankaran } 124825fdd593SJeykumar Sankaran if (dpu_crtc->vblank_cb_count) { 124925fdd593SJeykumar Sankaran ktime_t diff = ktime_sub(ktime_get(), dpu_crtc->vblank_cb_time); 125025fdd593SJeykumar Sankaran s64 diff_ms = ktime_to_ms(diff); 125125fdd593SJeykumar Sankaran s64 fps = diff_ms ? div_s64( 125225fdd593SJeykumar Sankaran dpu_crtc->vblank_cb_count * 1000, diff_ms) : 0; 125325fdd593SJeykumar Sankaran 125425fdd593SJeykumar Sankaran seq_printf(s, 125525fdd593SJeykumar Sankaran "vblank fps:%lld count:%u total:%llums total_framecount:%llu\n", 125625fdd593SJeykumar Sankaran fps, dpu_crtc->vblank_cb_count, 125725fdd593SJeykumar Sankaran ktime_to_ms(diff), dpu_crtc->play_count); 125825fdd593SJeykumar Sankaran 125925fdd593SJeykumar Sankaran /* reset time & count for next measurement */ 126025fdd593SJeykumar Sankaran dpu_crtc->vblank_cb_count = 0; 126125fdd593SJeykumar Sankaran dpu_crtc->vblank_cb_time = ktime_set(0, 0); 126225fdd593SJeykumar Sankaran } 126325fdd593SJeykumar Sankaran 12649222cdd2SJeykumar Sankaran drm_modeset_unlock_all(crtc->dev); 126525fdd593SJeykumar Sankaran 126625fdd593SJeykumar Sankaran return 0; 126725fdd593SJeykumar Sankaran } 126825fdd593SJeykumar Sankaran 1269341a361cSQinglang Miao DEFINE_SHOW_ATTRIBUTE(_dpu_debugfs_status); 127025fdd593SJeykumar Sankaran 127125fdd593SJeykumar Sankaran static int dpu_crtc_debugfs_state_show(struct seq_file *s, void *v) 127225fdd593SJeykumar Sankaran { 127325fdd593SJeykumar Sankaran struct drm_crtc *crtc = (struct drm_crtc *) s->private; 127425fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 127525fdd593SJeykumar Sankaran 127625fdd593SJeykumar Sankaran seq_printf(s, "client type: %d\n", dpu_crtc_get_client_type(crtc)); 127725fdd593SJeykumar Sankaran seq_printf(s, "intf_mode: %d\n", dpu_crtc_get_intf_mode(crtc)); 127825fdd593SJeykumar Sankaran seq_printf(s, "core_clk_rate: %llu\n", 127925fdd593SJeykumar Sankaran dpu_crtc->cur_perf.core_clk_rate); 1280cb88482eSJayant Shekhar seq_printf(s, "bw_ctl: %llu\n", dpu_crtc->cur_perf.bw_ctl); 1281cb88482eSJayant Shekhar seq_printf(s, "max_per_pipe_ib: %llu\n", 1282cb88482eSJayant Shekhar dpu_crtc->cur_perf.max_per_pipe_ib); 128325fdd593SJeykumar Sankaran 128425fdd593SJeykumar Sankaran return 0; 128525fdd593SJeykumar Sankaran } 1286341a361cSQinglang Miao DEFINE_SHOW_ATTRIBUTE(dpu_crtc_debugfs_state); 128725fdd593SJeykumar Sankaran 128825fdd593SJeykumar Sankaran static int _dpu_crtc_init_debugfs(struct drm_crtc *crtc) 128925fdd593SJeykumar Sankaran { 12903d688410SJordan Crouse struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 129125fdd593SJeykumar Sankaran 129225fdd593SJeykumar Sankaran dpu_crtc->debugfs_root = debugfs_create_dir(dpu_crtc->name, 129325fdd593SJeykumar Sankaran crtc->dev->primary->debugfs_root); 129425fdd593SJeykumar Sankaran 129525fdd593SJeykumar Sankaran debugfs_create_file("status", 0400, 129625fdd593SJeykumar Sankaran dpu_crtc->debugfs_root, 1297341a361cSQinglang Miao dpu_crtc, &_dpu_debugfs_status_fops); 129825fdd593SJeykumar Sankaran debugfs_create_file("state", 0600, 129925fdd593SJeykumar Sankaran dpu_crtc->debugfs_root, 130025fdd593SJeykumar Sankaran &dpu_crtc->base, 130125fdd593SJeykumar Sankaran &dpu_crtc_debugfs_state_fops); 130225fdd593SJeykumar Sankaran 130325fdd593SJeykumar Sankaran return 0; 130425fdd593SJeykumar Sankaran } 130525fdd593SJeykumar Sankaran #else 130625fdd593SJeykumar Sankaran static int _dpu_crtc_init_debugfs(struct drm_crtc *crtc) 130725fdd593SJeykumar Sankaran { 130825fdd593SJeykumar Sankaran return 0; 130925fdd593SJeykumar Sankaran } 131025fdd593SJeykumar Sankaran #endif /* CONFIG_DEBUG_FS */ 131125fdd593SJeykumar Sankaran 131225fdd593SJeykumar Sankaran static int dpu_crtc_late_register(struct drm_crtc *crtc) 131325fdd593SJeykumar Sankaran { 131425fdd593SJeykumar Sankaran return _dpu_crtc_init_debugfs(crtc); 131525fdd593SJeykumar Sankaran } 131625fdd593SJeykumar Sankaran 131725fdd593SJeykumar Sankaran static void dpu_crtc_early_unregister(struct drm_crtc *crtc) 131825fdd593SJeykumar Sankaran { 13193d688410SJordan Crouse struct dpu_crtc *dpu_crtc = to_dpu_crtc(crtc); 13203d688410SJordan Crouse 13213d688410SJordan Crouse debugfs_remove_recursive(dpu_crtc->debugfs_root); 132225fdd593SJeykumar Sankaran } 132325fdd593SJeykumar Sankaran 132425fdd593SJeykumar Sankaran static const struct drm_crtc_funcs dpu_crtc_funcs = { 132525fdd593SJeykumar Sankaran .set_config = drm_atomic_helper_set_config, 132625fdd593SJeykumar Sankaran .destroy = dpu_crtc_destroy, 132725fdd593SJeykumar Sankaran .page_flip = drm_atomic_helper_page_flip, 132825fdd593SJeykumar Sankaran .reset = dpu_crtc_reset, 132925fdd593SJeykumar Sankaran .atomic_duplicate_state = dpu_crtc_duplicate_state, 133025fdd593SJeykumar Sankaran .atomic_destroy_state = dpu_crtc_destroy_state, 133125fdd593SJeykumar Sankaran .late_register = dpu_crtc_late_register, 133225fdd593SJeykumar Sankaran .early_unregister = dpu_crtc_early_unregister, 133376e8cfd8SThomas Zimmermann .enable_vblank = msm_crtc_enable_vblank, 133476e8cfd8SThomas Zimmermann .disable_vblank = msm_crtc_disable_vblank, 133573743e72SKalyan Thota .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, 133673743e72SKalyan Thota .get_vblank_counter = dpu_crtc_get_vblank_counter, 133725fdd593SJeykumar Sankaran }; 133825fdd593SJeykumar Sankaran 133925fdd593SJeykumar Sankaran static const struct drm_crtc_helper_funcs dpu_crtc_helper_funcs = { 1340f7aafc8dSSean Paul .atomic_disable = dpu_crtc_disable, 134125fdd593SJeykumar Sankaran .atomic_enable = dpu_crtc_enable, 134225fdd593SJeykumar Sankaran .atomic_check = dpu_crtc_atomic_check, 134325fdd593SJeykumar Sankaran .atomic_begin = dpu_crtc_atomic_begin, 134425fdd593SJeykumar Sankaran .atomic_flush = dpu_crtc_atomic_flush, 134573743e72SKalyan Thota .get_scanout_position = dpu_crtc_get_scanout_position, 134625fdd593SJeykumar Sankaran }; 134725fdd593SJeykumar Sankaran 134825fdd593SJeykumar Sankaran /* initialize crtc */ 134907ca1fc0SSravanthi Kollukuduru struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane, 135007ca1fc0SSravanthi Kollukuduru struct drm_plane *cursor) 135125fdd593SJeykumar Sankaran { 135225fdd593SJeykumar Sankaran struct drm_crtc *crtc = NULL; 135325fdd593SJeykumar Sankaran struct dpu_crtc *dpu_crtc = NULL; 1354c17aeda0SJordan Crouse int i; 135525fdd593SJeykumar Sankaran 135625fdd593SJeykumar Sankaran dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL); 135725fdd593SJeykumar Sankaran if (!dpu_crtc) 135825fdd593SJeykumar Sankaran return ERR_PTR(-ENOMEM); 135925fdd593SJeykumar Sankaran 136025fdd593SJeykumar Sankaran crtc = &dpu_crtc->base; 136125fdd593SJeykumar Sankaran crtc->dev = dev; 136225fdd593SJeykumar Sankaran 136325fdd593SJeykumar Sankaran spin_lock_init(&dpu_crtc->spin_lock); 136425fdd593SJeykumar Sankaran atomic_set(&dpu_crtc->frame_pending, 0); 136525fdd593SJeykumar Sankaran 136625fdd593SJeykumar Sankaran init_completion(&dpu_crtc->frame_done_comp); 136725fdd593SJeykumar Sankaran 136825fdd593SJeykumar Sankaran INIT_LIST_HEAD(&dpu_crtc->frame_event_list); 136925fdd593SJeykumar Sankaran 137025fdd593SJeykumar Sankaran for (i = 0; i < ARRAY_SIZE(dpu_crtc->frame_events); i++) { 137125fdd593SJeykumar Sankaran INIT_LIST_HEAD(&dpu_crtc->frame_events[i].list); 137225fdd593SJeykumar Sankaran list_add(&dpu_crtc->frame_events[i].list, 137325fdd593SJeykumar Sankaran &dpu_crtc->frame_event_list); 137425fdd593SJeykumar Sankaran kthread_init_work(&dpu_crtc->frame_events[i].work, 137525fdd593SJeykumar Sankaran dpu_crtc_frame_event_work); 137625fdd593SJeykumar Sankaran } 137725fdd593SJeykumar Sankaran 137807ca1fc0SSravanthi Kollukuduru drm_crtc_init_with_planes(dev, crtc, plane, cursor, &dpu_crtc_funcs, 137925fdd593SJeykumar Sankaran NULL); 138025fdd593SJeykumar Sankaran 138125fdd593SJeykumar Sankaran drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs); 138225fdd593SJeykumar Sankaran 13834259ff7aSKalyan Thota drm_crtc_enable_color_mgmt(crtc, 0, true, 0); 13844259ff7aSKalyan Thota 138525fdd593SJeykumar Sankaran /* save user friendly CRTC name for later */ 138625fdd593SJeykumar Sankaran snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id); 138725fdd593SJeykumar Sankaran 138825fdd593SJeykumar Sankaran /* initialize event handling */ 1389c17aeda0SJordan Crouse spin_lock_init(&dpu_crtc->event_lock); 139025fdd593SJeykumar Sankaran 13915b702d78SStephen Boyd DRM_DEBUG_KMS("%s: successfully initialized crtc\n", dpu_crtc->name); 139225fdd593SJeykumar Sankaran return crtc; 139325fdd593SJeykumar Sankaran } 1394