Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2016 Advanced Micro Devices, Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: AMD
0023  *
0024  */
0025 
0026 #include <linux/delay.h>
0027 #include "dm_services.h"
0028 #include "basics/dc_common.h"
0029 #include "core_types.h"
0030 #include "resource.h"
0031 #include "custom_float.h"
0032 #include "dcn10_hw_sequencer.h"
0033 #include "dcn10_hw_sequencer_debug.h"
0034 #include "dce/dce_hwseq.h"
0035 #include "abm.h"
0036 #include "dmcu.h"
0037 #include "dcn10_optc.h"
0038 #include "dcn10_dpp.h"
0039 #include "dcn10_mpc.h"
0040 #include "timing_generator.h"
0041 #include "opp.h"
0042 #include "ipp.h"
0043 #include "mpc.h"
0044 #include "reg_helper.h"
0045 #include "dcn10_hubp.h"
0046 #include "dcn10_hubbub.h"
0047 #include "dcn10_cm_common.h"
0048 #include "dc_link_dp.h"
0049 #include "dccg.h"
0050 #include "clk_mgr.h"
0051 #include "link_hwss.h"
0052 #include "dpcd_defs.h"
0053 #include "dsc.h"
0054 #include "dce/dmub_psr.h"
0055 #include "dc_dmub_srv.h"
0056 #include "dce/dmub_hw_lock_mgr.h"
0057 #include "dc_trace.h"
0058 #include "dce/dmub_outbox.h"
0059 #include "inc/dc_link_dp.h"
0060 #include "inc/link_dpcd.h"
0061 
0062 #define DC_LOGGER_INIT(logger)
0063 
0064 #define CTX \
0065     hws->ctx
0066 #define REG(reg)\
0067     hws->regs->reg
0068 
0069 #undef FN
0070 #define FN(reg_name, field_name) \
0071     hws->shifts->field_name, hws->masks->field_name
0072 
0073 /*print is 17 wide, first two characters are spaces*/
0074 #define DTN_INFO_MICRO_SEC(ref_cycle) \
0075     print_microsec(dc_ctx, log_ctx, ref_cycle)
0076 
0077 #define GAMMA_HW_POINTS_NUM 256
0078 
0079 #define PGFSM_POWER_ON 0
0080 #define PGFSM_POWER_OFF 2
0081 
0082 static void print_microsec(struct dc_context *dc_ctx,
0083                struct dc_log_buffer_ctx *log_ctx,
0084                uint32_t ref_cycle)
0085 {
0086     const uint32_t ref_clk_mhz = dc_ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000;
0087     static const unsigned int frac = 1000;
0088     uint32_t us_x10 = (ref_cycle * frac) / ref_clk_mhz;
0089 
0090     DTN_INFO("  %11d.%03d",
0091             us_x10 / frac,
0092             us_x10 % frac);
0093 }
0094 
0095 void dcn10_lock_all_pipes(struct dc *dc,
0096     struct dc_state *context,
0097     bool lock)
0098 {
0099     struct pipe_ctx *pipe_ctx;
0100     struct timing_generator *tg;
0101     int i;
0102 
0103     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0104         pipe_ctx = &context->res_ctx.pipe_ctx[i];
0105         tg = pipe_ctx->stream_res.tg;
0106 
0107         /*
0108          * Only lock the top pipe's tg to prevent redundant
0109          * (un)locking. Also skip if pipe is disabled.
0110          */
0111         if (pipe_ctx->top_pipe ||
0112             !pipe_ctx->stream ||
0113             !pipe_ctx->plane_state ||
0114             !tg->funcs->is_tg_enabled(tg))
0115             continue;
0116 
0117         if (lock)
0118             dc->hwss.pipe_control_lock(dc, pipe_ctx, true);
0119         else
0120             dc->hwss.pipe_control_lock(dc, pipe_ctx, false);
0121     }
0122 }
0123 
0124 static void log_mpc_crc(struct dc *dc,
0125     struct dc_log_buffer_ctx *log_ctx)
0126 {
0127     struct dc_context *dc_ctx = dc->ctx;
0128     struct dce_hwseq *hws = dc->hwseq;
0129 
0130     if (REG(MPC_CRC_RESULT_GB))
0131         DTN_INFO("MPC_CRC_RESULT_GB:%d MPC_CRC_RESULT_C:%d MPC_CRC_RESULT_AR:%d\n",
0132         REG_READ(MPC_CRC_RESULT_GB), REG_READ(MPC_CRC_RESULT_C), REG_READ(MPC_CRC_RESULT_AR));
0133     if (REG(DPP_TOP0_DPP_CRC_VAL_B_A))
0134         DTN_INFO("DPP_TOP0_DPP_CRC_VAL_B_A:%d DPP_TOP0_DPP_CRC_VAL_R_G:%d\n",
0135         REG_READ(DPP_TOP0_DPP_CRC_VAL_B_A), REG_READ(DPP_TOP0_DPP_CRC_VAL_R_G));
0136 }
0137 
0138 static void dcn10_log_hubbub_state(struct dc *dc,
0139                    struct dc_log_buffer_ctx *log_ctx)
0140 {
0141     struct dc_context *dc_ctx = dc->ctx;
0142     struct dcn_hubbub_wm wm;
0143     int i;
0144 
0145     memset(&wm, 0, sizeof(struct dcn_hubbub_wm));
0146     dc->res_pool->hubbub->funcs->wm_read_state(dc->res_pool->hubbub, &wm);
0147 
0148     DTN_INFO("HUBBUB WM:      data_urgent  pte_meta_urgent"
0149             "         sr_enter          sr_exit  dram_clk_change\n");
0150 
0151     for (i = 0; i < 4; i++) {
0152         struct dcn_hubbub_wm_set *s;
0153 
0154         s = &wm.sets[i];
0155         DTN_INFO("WM_Set[%d]:", s->wm_set);
0156         DTN_INFO_MICRO_SEC(s->data_urgent);
0157         DTN_INFO_MICRO_SEC(s->pte_meta_urgent);
0158         DTN_INFO_MICRO_SEC(s->sr_enter);
0159         DTN_INFO_MICRO_SEC(s->sr_exit);
0160         DTN_INFO_MICRO_SEC(s->dram_clk_chanage);
0161         DTN_INFO("\n");
0162     }
0163 
0164     DTN_INFO("\n");
0165 }
0166 
0167 static void dcn10_log_hubp_states(struct dc *dc, void *log_ctx)
0168 {
0169     struct dc_context *dc_ctx = dc->ctx;
0170     struct resource_pool *pool = dc->res_pool;
0171     int i;
0172 
0173     DTN_INFO(
0174         "HUBP:  format  addr_hi  width  height  rot  mir  sw_mode  dcc_en  blank_en  clock_en  ttu_dis  underflow   min_ttu_vblank       qos_low_wm      qos_high_wm\n");
0175     for (i = 0; i < pool->pipe_count; i++) {
0176         struct hubp *hubp = pool->hubps[i];
0177         struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state);
0178 
0179         hubp->funcs->hubp_read_state(hubp);
0180 
0181         if (!s->blank_en) {
0182             DTN_INFO("[%2d]:  %5xh  %6xh  %5d  %6d  %2xh  %2xh  %6xh  %6d  %8d  %8d  %7d  %8xh",
0183                     hubp->inst,
0184                     s->pixel_format,
0185                     s->inuse_addr_hi,
0186                     s->viewport_width,
0187                     s->viewport_height,
0188                     s->rotation_angle,
0189                     s->h_mirror_en,
0190                     s->sw_mode,
0191                     s->dcc_en,
0192                     s->blank_en,
0193                     s->clock_en,
0194                     s->ttu_disable,
0195                     s->underflow_status);
0196             DTN_INFO_MICRO_SEC(s->min_ttu_vblank);
0197             DTN_INFO_MICRO_SEC(s->qos_level_low_wm);
0198             DTN_INFO_MICRO_SEC(s->qos_level_high_wm);
0199             DTN_INFO("\n");
0200         }
0201     }
0202 
0203     DTN_INFO("\n=========RQ========\n");
0204     DTN_INFO("HUBP:  drq_exp_m  prq_exp_m  mrq_exp_m  crq_exp_m  plane1_ba  L:chunk_s  min_chu_s  meta_ch_s"
0205         "  min_m_c_s  dpte_gr_s  mpte_gr_s  swath_hei  pte_row_h  C:chunk_s  min_chu_s  meta_ch_s"
0206         "  min_m_c_s  dpte_gr_s  mpte_gr_s  swath_hei  pte_row_h\n");
0207     for (i = 0; i < pool->pipe_count; i++) {
0208         struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state);
0209         struct _vcs_dpi_display_rq_regs_st *rq_regs = &s->rq_regs;
0210 
0211         if (!s->blank_en)
0212             DTN_INFO("[%2d]:  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh\n",
0213                 pool->hubps[i]->inst, rq_regs->drq_expansion_mode, rq_regs->prq_expansion_mode, rq_regs->mrq_expansion_mode,
0214                 rq_regs->crq_expansion_mode, rq_regs->plane1_base_address, rq_regs->rq_regs_l.chunk_size,
0215                 rq_regs->rq_regs_l.min_chunk_size, rq_regs->rq_regs_l.meta_chunk_size,
0216                 rq_regs->rq_regs_l.min_meta_chunk_size, rq_regs->rq_regs_l.dpte_group_size,
0217                 rq_regs->rq_regs_l.mpte_group_size, rq_regs->rq_regs_l.swath_height,
0218                 rq_regs->rq_regs_l.pte_row_height_linear, rq_regs->rq_regs_c.chunk_size, rq_regs->rq_regs_c.min_chunk_size,
0219                 rq_regs->rq_regs_c.meta_chunk_size, rq_regs->rq_regs_c.min_meta_chunk_size,
0220                 rq_regs->rq_regs_c.dpte_group_size, rq_regs->rq_regs_c.mpte_group_size,
0221                 rq_regs->rq_regs_c.swath_height, rq_regs->rq_regs_c.pte_row_height_linear);
0222     }
0223 
0224     DTN_INFO("========DLG========\n");
0225     DTN_INFO("HUBP:  rc_hbe     dlg_vbe    min_d_y_n  rc_per_ht  rc_x_a_s "
0226             "  dst_y_a_s  dst_y_pf   dst_y_vvb  dst_y_rvb  dst_y_vfl  dst_y_rfl  rf_pix_fq"
0227             "  vratio_pf  vrat_pf_c  rc_pg_vbl  rc_pg_vbc  rc_mc_vbl  rc_mc_vbc  rc_pg_fll"
0228             "  rc_pg_flc  rc_mc_fll  rc_mc_flc  pr_nom_l   pr_nom_c   rc_pg_nl   rc_pg_nc "
0229             "  mr_nom_l   mr_nom_c   rc_mc_nl   rc_mc_nc   rc_ld_pl   rc_ld_pc   rc_ld_l  "
0230             "  rc_ld_c    cha_cur0   ofst_cur1  cha_cur1   vr_af_vc0  ddrq_limt  x_rt_dlay"
0231             "  x_rp_dlay  x_rr_sfl\n");
0232     for (i = 0; i < pool->pipe_count; i++) {
0233         struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state);
0234         struct _vcs_dpi_display_dlg_regs_st *dlg_regs = &s->dlg_attr;
0235 
0236         if (!s->blank_en)
0237             DTN_INFO("[%2d]:  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh"
0238                 "  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh"
0239                 "  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh\n",
0240                 pool->hubps[i]->inst, dlg_regs->refcyc_h_blank_end, dlg_regs->dlg_vblank_end, dlg_regs->min_dst_y_next_start,
0241                 dlg_regs->refcyc_per_htotal, dlg_regs->refcyc_x_after_scaler, dlg_regs->dst_y_after_scaler,
0242                 dlg_regs->dst_y_prefetch, dlg_regs->dst_y_per_vm_vblank, dlg_regs->dst_y_per_row_vblank,
0243                 dlg_regs->dst_y_per_vm_flip, dlg_regs->dst_y_per_row_flip, dlg_regs->ref_freq_to_pix_freq,
0244                 dlg_regs->vratio_prefetch, dlg_regs->vratio_prefetch_c, dlg_regs->refcyc_per_pte_group_vblank_l,
0245                 dlg_regs->refcyc_per_pte_group_vblank_c, dlg_regs->refcyc_per_meta_chunk_vblank_l,
0246                 dlg_regs->refcyc_per_meta_chunk_vblank_c, dlg_regs->refcyc_per_pte_group_flip_l,
0247                 dlg_regs->refcyc_per_pte_group_flip_c, dlg_regs->refcyc_per_meta_chunk_flip_l,
0248                 dlg_regs->refcyc_per_meta_chunk_flip_c, dlg_regs->dst_y_per_pte_row_nom_l,
0249                 dlg_regs->dst_y_per_pte_row_nom_c, dlg_regs->refcyc_per_pte_group_nom_l,
0250                 dlg_regs->refcyc_per_pte_group_nom_c, dlg_regs->dst_y_per_meta_row_nom_l,
0251                 dlg_regs->dst_y_per_meta_row_nom_c, dlg_regs->refcyc_per_meta_chunk_nom_l,
0252                 dlg_regs->refcyc_per_meta_chunk_nom_c, dlg_regs->refcyc_per_line_delivery_pre_l,
0253                 dlg_regs->refcyc_per_line_delivery_pre_c, dlg_regs->refcyc_per_line_delivery_l,
0254                 dlg_regs->refcyc_per_line_delivery_c, dlg_regs->chunk_hdl_adjust_cur0, dlg_regs->dst_y_offset_cur1,
0255                 dlg_regs->chunk_hdl_adjust_cur1, dlg_regs->vready_after_vcount0, dlg_regs->dst_y_delta_drq_limit,
0256                 dlg_regs->xfc_reg_transfer_delay, dlg_regs->xfc_reg_precharge_delay,
0257                 dlg_regs->xfc_reg_remote_surface_flip_latency);
0258     }
0259 
0260     DTN_INFO("========TTU========\n");
0261     DTN_INFO("HUBP:  qos_ll_wm  qos_lh_wm  mn_ttu_vb  qos_l_flp  rc_rd_p_l  rc_rd_l    rc_rd_p_c"
0262             "  rc_rd_c    rc_rd_c0   rc_rd_pc0  rc_rd_c1   rc_rd_pc1  qos_lf_l   qos_rds_l"
0263             "  qos_lf_c   qos_rds_c  qos_lf_c0  qos_rds_c0 qos_lf_c1  qos_rds_c1\n");
0264     for (i = 0; i < pool->pipe_count; i++) {
0265         struct dcn_hubp_state *s = &(TO_DCN10_HUBP(pool->hubps[i])->state);
0266         struct _vcs_dpi_display_ttu_regs_st *ttu_regs = &s->ttu_attr;
0267 
0268         if (!s->blank_en)
0269             DTN_INFO("[%2d]:  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh  %8xh\n",
0270                 pool->hubps[i]->inst, ttu_regs->qos_level_low_wm, ttu_regs->qos_level_high_wm, ttu_regs->min_ttu_vblank,
0271                 ttu_regs->qos_level_flip, ttu_regs->refcyc_per_req_delivery_pre_l, ttu_regs->refcyc_per_req_delivery_l,
0272                 ttu_regs->refcyc_per_req_delivery_pre_c, ttu_regs->refcyc_per_req_delivery_c, ttu_regs->refcyc_per_req_delivery_cur0,
0273                 ttu_regs->refcyc_per_req_delivery_pre_cur0, ttu_regs->refcyc_per_req_delivery_cur1,
0274                 ttu_regs->refcyc_per_req_delivery_pre_cur1, ttu_regs->qos_level_fixed_l, ttu_regs->qos_ramp_disable_l,
0275                 ttu_regs->qos_level_fixed_c, ttu_regs->qos_ramp_disable_c, ttu_regs->qos_level_fixed_cur0,
0276                 ttu_regs->qos_ramp_disable_cur0, ttu_regs->qos_level_fixed_cur1, ttu_regs->qos_ramp_disable_cur1);
0277     }
0278     DTN_INFO("\n");
0279 }
0280 
0281 void dcn10_log_hw_state(struct dc *dc,
0282     struct dc_log_buffer_ctx *log_ctx)
0283 {
0284     struct dc_context *dc_ctx = dc->ctx;
0285     struct resource_pool *pool = dc->res_pool;
0286     int i;
0287 
0288     DTN_INFO_BEGIN();
0289 
0290     dcn10_log_hubbub_state(dc, log_ctx);
0291 
0292     dcn10_log_hubp_states(dc, log_ctx);
0293 
0294     DTN_INFO("DPP:    IGAM format  IGAM mode    DGAM mode    RGAM mode"
0295             "  GAMUT mode  C11 C12   C13 C14   C21 C22   C23 C24   "
0296             "C31 C32   C33 C34\n");
0297     for (i = 0; i < pool->pipe_count; i++) {
0298         struct dpp *dpp = pool->dpps[i];
0299         struct dcn_dpp_state s = {0};
0300 
0301         dpp->funcs->dpp_read_state(dpp, &s);
0302 
0303         if (!s.is_enabled)
0304             continue;
0305 
0306         DTN_INFO("[%2d]:  %11xh  %-11s  %-11s  %-11s"
0307                 "%8x    %08xh %08xh %08xh %08xh %08xh %08xh",
0308                 dpp->inst,
0309                 s.igam_input_format,
0310                 (s.igam_lut_mode == 0) ? "BypassFixed" :
0311                     ((s.igam_lut_mode == 1) ? "BypassFloat" :
0312                     ((s.igam_lut_mode == 2) ? "RAM" :
0313                     ((s.igam_lut_mode == 3) ? "RAM" :
0314                                  "Unknown"))),
0315                 (s.dgam_lut_mode == 0) ? "Bypass" :
0316                     ((s.dgam_lut_mode == 1) ? "sRGB" :
0317                     ((s.dgam_lut_mode == 2) ? "Ycc" :
0318                     ((s.dgam_lut_mode == 3) ? "RAM" :
0319                     ((s.dgam_lut_mode == 4) ? "RAM" :
0320                                  "Unknown")))),
0321                 (s.rgam_lut_mode == 0) ? "Bypass" :
0322                     ((s.rgam_lut_mode == 1) ? "sRGB" :
0323                     ((s.rgam_lut_mode == 2) ? "Ycc" :
0324                     ((s.rgam_lut_mode == 3) ? "RAM" :
0325                     ((s.rgam_lut_mode == 4) ? "RAM" :
0326                                  "Unknown")))),
0327                 s.gamut_remap_mode,
0328                 s.gamut_remap_c11_c12,
0329                 s.gamut_remap_c13_c14,
0330                 s.gamut_remap_c21_c22,
0331                 s.gamut_remap_c23_c24,
0332                 s.gamut_remap_c31_c32,
0333                 s.gamut_remap_c33_c34);
0334         DTN_INFO("\n");
0335     }
0336     DTN_INFO("\n");
0337 
0338     DTN_INFO("MPCC:  OPP  DPP  MPCCBOT  MODE  ALPHA_MODE  PREMULT  OVERLAP_ONLY  IDLE\n");
0339     for (i = 0; i < pool->pipe_count; i++) {
0340         struct mpcc_state s = {0};
0341 
0342         pool->mpc->funcs->read_mpcc_state(pool->mpc, i, &s);
0343         if (s.opp_id != 0xf)
0344             DTN_INFO("[%2d]:  %2xh  %2xh  %6xh  %4d  %10d  %7d  %12d  %4d\n",
0345                 i, s.opp_id, s.dpp_id, s.bot_mpcc_id,
0346                 s.mode, s.alpha_mode, s.pre_multiplied_alpha, s.overlap_only,
0347                 s.idle);
0348     }
0349     DTN_INFO("\n");
0350 
0351     DTN_INFO("OTG:  v_bs  v_be  v_ss  v_se  vpol  vmax  vmin  vmax_sel  vmin_sel  h_bs  h_be  h_ss  h_se  hpol  htot  vtot  underflow blank_en\n");
0352 
0353     for (i = 0; i < pool->timing_generator_count; i++) {
0354         struct timing_generator *tg = pool->timing_generators[i];
0355         struct dcn_otg_state s = {0};
0356         /* Read shared OTG state registers for all DCNx */
0357         optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s);
0358 
0359         /*
0360          * For DCN2 and greater, a register on the OPP is used to
0361          * determine if the CRTC is blanked instead of the OTG. So use
0362          * dpg_is_blanked() if exists, otherwise fallback on otg.
0363          *
0364          * TODO: Implement DCN-specific read_otg_state hooks.
0365          */
0366         if (pool->opps[i]->funcs->dpg_is_blanked)
0367             s.blank_enabled = pool->opps[i]->funcs->dpg_is_blanked(pool->opps[i]);
0368         else
0369             s.blank_enabled = tg->funcs->is_blanked(tg);
0370 
0371         //only print if OTG master is enabled
0372         if ((s.otg_enabled & 1) == 0)
0373             continue;
0374 
0375         DTN_INFO("[%d]: %5d %5d %5d %5d %5d %5d %5d %9d %9d %5d %5d %5d %5d %5d %5d %5d  %9d %8d\n",
0376                 tg->inst,
0377                 s.v_blank_start,
0378                 s.v_blank_end,
0379                 s.v_sync_a_start,
0380                 s.v_sync_a_end,
0381                 s.v_sync_a_pol,
0382                 s.v_total_max,
0383                 s.v_total_min,
0384                 s.v_total_max_sel,
0385                 s.v_total_min_sel,
0386                 s.h_blank_start,
0387                 s.h_blank_end,
0388                 s.h_sync_a_start,
0389                 s.h_sync_a_end,
0390                 s.h_sync_a_pol,
0391                 s.h_total,
0392                 s.v_total,
0393                 s.underflow_occurred_status,
0394                 s.blank_enabled);
0395 
0396         // Clear underflow for debug purposes
0397         // We want to keep underflow sticky bit on for the longevity tests outside of test environment.
0398         // This function is called only from Windows or Diags test environment, hence it's safe to clear
0399         // it from here without affecting the original intent.
0400         tg->funcs->clear_optc_underflow(tg);
0401     }
0402     DTN_INFO("\n");
0403 
0404     // dcn_dsc_state struct field bytes_per_pixel was renamed to bits_per_pixel
0405     // TODO: Update golden log header to reflect this name change
0406     DTN_INFO("DSC: CLOCK_EN  SLICE_WIDTH  Bytes_pp\n");
0407     for (i = 0; i < pool->res_cap->num_dsc; i++) {
0408         struct display_stream_compressor *dsc = pool->dscs[i];
0409         struct dcn_dsc_state s = {0};
0410 
0411         dsc->funcs->dsc_read_state(dsc, &s);
0412         DTN_INFO("[%d]: %-9d %-12d %-10d\n",
0413         dsc->inst,
0414             s.dsc_clock_en,
0415             s.dsc_slice_width,
0416             s.dsc_bits_per_pixel);
0417         DTN_INFO("\n");
0418     }
0419     DTN_INFO("\n");
0420 
0421     DTN_INFO("S_ENC: DSC_MODE  SEC_GSP7_LINE_NUM"
0422             "  VBID6_LINE_REFERENCE  VBID6_LINE_NUM  SEC_GSP7_ENABLE  SEC_STREAM_ENABLE\n");
0423     for (i = 0; i < pool->stream_enc_count; i++) {
0424         struct stream_encoder *enc = pool->stream_enc[i];
0425         struct enc_state s = {0};
0426 
0427         if (enc->funcs->enc_read_state) {
0428             enc->funcs->enc_read_state(enc, &s);
0429             DTN_INFO("[%-3d]: %-9d %-18d %-21d %-15d %-16d %-17d\n",
0430                 enc->id,
0431                 s.dsc_mode,
0432                 s.sec_gsp_pps_line_num,
0433                 s.vbid6_line_reference,
0434                 s.vbid6_line_num,
0435                 s.sec_gsp_pps_enable,
0436                 s.sec_stream_enable);
0437             DTN_INFO("\n");
0438         }
0439     }
0440     DTN_INFO("\n");
0441 
0442     DTN_INFO("L_ENC: DPHY_FEC_EN  DPHY_FEC_READY_SHADOW  DPHY_FEC_ACTIVE_STATUS  DP_LINK_TRAINING_COMPLETE\n");
0443     for (i = 0; i < dc->link_count; i++) {
0444         struct link_encoder *lenc = dc->links[i]->link_enc;
0445 
0446         struct link_enc_state s = {0};
0447 
0448         if (lenc && lenc->funcs->read_state) {
0449             lenc->funcs->read_state(lenc, &s);
0450             DTN_INFO("[%-3d]: %-12d %-22d %-22d %-25d\n",
0451                 i,
0452                 s.dphy_fec_en,
0453                 s.dphy_fec_ready_shadow,
0454                 s.dphy_fec_active_status,
0455                 s.dp_link_training_complete);
0456             DTN_INFO("\n");
0457         }
0458     }
0459     DTN_INFO("\n");
0460 
0461     DTN_INFO("\nCALCULATED Clocks: dcfclk_khz:%d  dcfclk_deep_sleep_khz:%d  dispclk_khz:%d\n"
0462         "dppclk_khz:%d  max_supported_dppclk_khz:%d  fclk_khz:%d  socclk_khz:%d\n\n",
0463             dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_khz,
0464             dc->current_state->bw_ctx.bw.dcn.clk.dcfclk_deep_sleep_khz,
0465             dc->current_state->bw_ctx.bw.dcn.clk.dispclk_khz,
0466             dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz,
0467             dc->current_state->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz,
0468             dc->current_state->bw_ctx.bw.dcn.clk.fclk_khz,
0469             dc->current_state->bw_ctx.bw.dcn.clk.socclk_khz);
0470 
0471     log_mpc_crc(dc, log_ctx);
0472 
0473     {
0474         if (pool->hpo_dp_stream_enc_count > 0) {
0475             DTN_INFO("DP HPO S_ENC:  Enabled  OTG   Format   Depth   Vid   SDP   Compressed  Link\n");
0476             for (i = 0; i < pool->hpo_dp_stream_enc_count; i++) {
0477                 struct hpo_dp_stream_encoder_state hpo_dp_se_state = {0};
0478                 struct hpo_dp_stream_encoder *hpo_dp_stream_enc = pool->hpo_dp_stream_enc[i];
0479 
0480                 if (hpo_dp_stream_enc && hpo_dp_stream_enc->funcs->read_state) {
0481                     hpo_dp_stream_enc->funcs->read_state(hpo_dp_stream_enc, &hpo_dp_se_state);
0482 
0483                     DTN_INFO("[%d]:                 %d    %d   %6s       %d     %d     %d            %d     %d\n",
0484                             hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0,
0485                             hpo_dp_se_state.stream_enc_enabled,
0486                             hpo_dp_se_state.otg_inst,
0487                             (hpo_dp_se_state.pixel_encoding == 0) ? "4:4:4" :
0488                                     ((hpo_dp_se_state.pixel_encoding == 1) ? "4:2:2" :
0489                                     (hpo_dp_se_state.pixel_encoding == 2) ? "4:2:0" : "Y-Only"),
0490                             (hpo_dp_se_state.component_depth == 0) ? 6 :
0491                                     ((hpo_dp_se_state.component_depth == 1) ? 8 :
0492                                     (hpo_dp_se_state.component_depth == 2) ? 10 : 12),
0493                             hpo_dp_se_state.vid_stream_enabled,
0494                             hpo_dp_se_state.sdp_enabled,
0495                             hpo_dp_se_state.compressed_format,
0496                             hpo_dp_se_state.mapped_to_link_enc);
0497                 }
0498             }
0499 
0500             DTN_INFO("\n");
0501         }
0502 
0503         /* log DP HPO L_ENC section if any hpo_dp_link_enc exists */
0504         if (pool->hpo_dp_link_enc_count) {
0505             DTN_INFO("DP HPO L_ENC:  Enabled  Mode   Lanes   Stream  Slots   VC Rate X    VC Rate Y\n");
0506 
0507             for (i = 0; i < pool->hpo_dp_link_enc_count; i++) {
0508                 struct hpo_dp_link_encoder *hpo_dp_link_enc = pool->hpo_dp_link_enc[i];
0509                 struct hpo_dp_link_enc_state hpo_dp_le_state = {0};
0510 
0511                 if (hpo_dp_link_enc->funcs->read_state) {
0512                     hpo_dp_link_enc->funcs->read_state(hpo_dp_link_enc, &hpo_dp_le_state);
0513                     DTN_INFO("[%d]:                 %d  %6s     %d        %d      %d     %d     %d\n",
0514                             hpo_dp_link_enc->inst,
0515                             hpo_dp_le_state.link_enc_enabled,
0516                             (hpo_dp_le_state.link_mode == 0) ? "TPS1" :
0517                                     (hpo_dp_le_state.link_mode == 1) ? "TPS2" :
0518                                     (hpo_dp_le_state.link_mode == 2) ? "ACTIVE" : "TEST",
0519                             hpo_dp_le_state.lane_count,
0520                             hpo_dp_le_state.stream_src[0],
0521                             hpo_dp_le_state.slot_count[0],
0522                             hpo_dp_le_state.vc_rate_x[0],
0523                             hpo_dp_le_state.vc_rate_y[0]);
0524                     DTN_INFO("\n");
0525                 }
0526             }
0527 
0528             DTN_INFO("\n");
0529         }
0530     }
0531 
0532     DTN_INFO_END();
0533 }
0534 
0535 bool dcn10_did_underflow_occur(struct dc *dc, struct pipe_ctx *pipe_ctx)
0536 {
0537     struct hubp *hubp = pipe_ctx->plane_res.hubp;
0538     struct timing_generator *tg = pipe_ctx->stream_res.tg;
0539 
0540     if (tg->funcs->is_optc_underflow_occurred(tg)) {
0541         tg->funcs->clear_optc_underflow(tg);
0542         return true;
0543     }
0544 
0545     if (hubp->funcs->hubp_get_underflow_status(hubp)) {
0546         hubp->funcs->hubp_clear_underflow(hubp);
0547         return true;
0548     }
0549     return false;
0550 }
0551 
0552 void dcn10_enable_power_gating_plane(
0553     struct dce_hwseq *hws,
0554     bool enable)
0555 {
0556     bool force_on = true; /* disable power gating */
0557 
0558     if (enable)
0559         force_on = false;
0560 
0561     /* DCHUBP0/1/2/3 */
0562     REG_UPDATE(DOMAIN0_PG_CONFIG, DOMAIN0_POWER_FORCEON, force_on);
0563     REG_UPDATE(DOMAIN2_PG_CONFIG, DOMAIN2_POWER_FORCEON, force_on);
0564     REG_UPDATE(DOMAIN4_PG_CONFIG, DOMAIN4_POWER_FORCEON, force_on);
0565     REG_UPDATE(DOMAIN6_PG_CONFIG, DOMAIN6_POWER_FORCEON, force_on);
0566 
0567     /* DPP0/1/2/3 */
0568     REG_UPDATE(DOMAIN1_PG_CONFIG, DOMAIN1_POWER_FORCEON, force_on);
0569     REG_UPDATE(DOMAIN3_PG_CONFIG, DOMAIN3_POWER_FORCEON, force_on);
0570     REG_UPDATE(DOMAIN5_PG_CONFIG, DOMAIN5_POWER_FORCEON, force_on);
0571     REG_UPDATE(DOMAIN7_PG_CONFIG, DOMAIN7_POWER_FORCEON, force_on);
0572 }
0573 
0574 void dcn10_disable_vga(
0575     struct dce_hwseq *hws)
0576 {
0577     unsigned int in_vga1_mode = 0;
0578     unsigned int in_vga2_mode = 0;
0579     unsigned int in_vga3_mode = 0;
0580     unsigned int in_vga4_mode = 0;
0581 
0582     REG_GET(D1VGA_CONTROL, D1VGA_MODE_ENABLE, &in_vga1_mode);
0583     REG_GET(D2VGA_CONTROL, D2VGA_MODE_ENABLE, &in_vga2_mode);
0584     REG_GET(D3VGA_CONTROL, D3VGA_MODE_ENABLE, &in_vga3_mode);
0585     REG_GET(D4VGA_CONTROL, D4VGA_MODE_ENABLE, &in_vga4_mode);
0586 
0587     if (in_vga1_mode == 0 && in_vga2_mode == 0 &&
0588             in_vga3_mode == 0 && in_vga4_mode == 0)
0589         return;
0590 
0591     REG_WRITE(D1VGA_CONTROL, 0);
0592     REG_WRITE(D2VGA_CONTROL, 0);
0593     REG_WRITE(D3VGA_CONTROL, 0);
0594     REG_WRITE(D4VGA_CONTROL, 0);
0595 
0596     /* HW Engineer's Notes:
0597      *  During switch from vga->extended, if we set the VGA_TEST_ENABLE and
0598      *  then hit the VGA_TEST_RENDER_START, then the DCHUBP timing gets updated correctly.
0599      *
0600      *  Then vBIOS will have it poll for the VGA_TEST_RENDER_DONE and unset
0601      *  VGA_TEST_ENABLE, to leave it in the same state as before.
0602      */
0603     REG_UPDATE(VGA_TEST_CONTROL, VGA_TEST_ENABLE, 1);
0604     REG_UPDATE(VGA_TEST_CONTROL, VGA_TEST_RENDER_START, 1);
0605 }
0606 
0607 /**
0608  * dcn10_dpp_pg_control - DPP power gate control.
0609  *
0610  * @hws: dce_hwseq reference.
0611  * @dpp_inst: DPP instance reference.
0612  * @power_on: true if we want to enable power gate, false otherwise.
0613  *
0614  * Enable or disable power gate in the specific DPP instance.
0615  */
0616 void dcn10_dpp_pg_control(
0617         struct dce_hwseq *hws,
0618         unsigned int dpp_inst,
0619         bool power_on)
0620 {
0621     uint32_t power_gate = power_on ? 0 : 1;
0622     uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF;
0623 
0624     if (hws->ctx->dc->debug.disable_dpp_power_gate)
0625         return;
0626     if (REG(DOMAIN1_PG_CONFIG) == 0)
0627         return;
0628 
0629     switch (dpp_inst) {
0630     case 0: /* DPP0 */
0631         REG_UPDATE(DOMAIN1_PG_CONFIG,
0632                 DOMAIN1_POWER_GATE, power_gate);
0633 
0634         REG_WAIT(DOMAIN1_PG_STATUS,
0635                 DOMAIN1_PGFSM_PWR_STATUS, pwr_status,
0636                 1, 1000);
0637         break;
0638     case 1: /* DPP1 */
0639         REG_UPDATE(DOMAIN3_PG_CONFIG,
0640                 DOMAIN3_POWER_GATE, power_gate);
0641 
0642         REG_WAIT(DOMAIN3_PG_STATUS,
0643                 DOMAIN3_PGFSM_PWR_STATUS, pwr_status,
0644                 1, 1000);
0645         break;
0646     case 2: /* DPP2 */
0647         REG_UPDATE(DOMAIN5_PG_CONFIG,
0648                 DOMAIN5_POWER_GATE, power_gate);
0649 
0650         REG_WAIT(DOMAIN5_PG_STATUS,
0651                 DOMAIN5_PGFSM_PWR_STATUS, pwr_status,
0652                 1, 1000);
0653         break;
0654     case 3: /* DPP3 */
0655         REG_UPDATE(DOMAIN7_PG_CONFIG,
0656                 DOMAIN7_POWER_GATE, power_gate);
0657 
0658         REG_WAIT(DOMAIN7_PG_STATUS,
0659                 DOMAIN7_PGFSM_PWR_STATUS, pwr_status,
0660                 1, 1000);
0661         break;
0662     default:
0663         BREAK_TO_DEBUGGER();
0664         break;
0665     }
0666 }
0667 
0668 /**
0669  * dcn10_hubp_pg_control - HUBP power gate control.
0670  *
0671  * @hws: dce_hwseq reference.
0672  * @hubp_inst: DPP instance reference.
0673  * @power_on: true if we want to enable power gate, false otherwise.
0674  *
0675  * Enable or disable power gate in the specific HUBP instance.
0676  */
0677 void dcn10_hubp_pg_control(
0678         struct dce_hwseq *hws,
0679         unsigned int hubp_inst,
0680         bool power_on)
0681 {
0682     uint32_t power_gate = power_on ? 0 : 1;
0683     uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF;
0684 
0685     if (hws->ctx->dc->debug.disable_hubp_power_gate)
0686         return;
0687     if (REG(DOMAIN0_PG_CONFIG) == 0)
0688         return;
0689 
0690     switch (hubp_inst) {
0691     case 0: /* DCHUBP0 */
0692         REG_UPDATE(DOMAIN0_PG_CONFIG,
0693                 DOMAIN0_POWER_GATE, power_gate);
0694 
0695         REG_WAIT(DOMAIN0_PG_STATUS,
0696                 DOMAIN0_PGFSM_PWR_STATUS, pwr_status,
0697                 1, 1000);
0698         break;
0699     case 1: /* DCHUBP1 */
0700         REG_UPDATE(DOMAIN2_PG_CONFIG,
0701                 DOMAIN2_POWER_GATE, power_gate);
0702 
0703         REG_WAIT(DOMAIN2_PG_STATUS,
0704                 DOMAIN2_PGFSM_PWR_STATUS, pwr_status,
0705                 1, 1000);
0706         break;
0707     case 2: /* DCHUBP2 */
0708         REG_UPDATE(DOMAIN4_PG_CONFIG,
0709                 DOMAIN4_POWER_GATE, power_gate);
0710 
0711         REG_WAIT(DOMAIN4_PG_STATUS,
0712                 DOMAIN4_PGFSM_PWR_STATUS, pwr_status,
0713                 1, 1000);
0714         break;
0715     case 3: /* DCHUBP3 */
0716         REG_UPDATE(DOMAIN6_PG_CONFIG,
0717                 DOMAIN6_POWER_GATE, power_gate);
0718 
0719         REG_WAIT(DOMAIN6_PG_STATUS,
0720                 DOMAIN6_PGFSM_PWR_STATUS, pwr_status,
0721                 1, 1000);
0722         break;
0723     default:
0724         BREAK_TO_DEBUGGER();
0725         break;
0726     }
0727 }
0728 
0729 static void power_on_plane(
0730     struct dce_hwseq *hws,
0731     int plane_id)
0732 {
0733     DC_LOGGER_INIT(hws->ctx->logger);
0734     if (REG(DC_IP_REQUEST_CNTL)) {
0735         REG_SET(DC_IP_REQUEST_CNTL, 0,
0736                 IP_REQUEST_EN, 1);
0737 
0738         if (hws->funcs.dpp_pg_control)
0739             hws->funcs.dpp_pg_control(hws, plane_id, true);
0740 
0741         if (hws->funcs.hubp_pg_control)
0742             hws->funcs.hubp_pg_control(hws, plane_id, true);
0743 
0744         REG_SET(DC_IP_REQUEST_CNTL, 0,
0745                 IP_REQUEST_EN, 0);
0746         DC_LOG_DEBUG(
0747                 "Un-gated front end for pipe %d\n", plane_id);
0748     }
0749 }
0750 
0751 static void undo_DEGVIDCN10_253_wa(struct dc *dc)
0752 {
0753     struct dce_hwseq *hws = dc->hwseq;
0754     struct hubp *hubp = dc->res_pool->hubps[0];
0755 
0756     if (!hws->wa_state.DEGVIDCN10_253_applied)
0757         return;
0758 
0759     hubp->funcs->set_blank(hubp, true);
0760 
0761     REG_SET(DC_IP_REQUEST_CNTL, 0,
0762             IP_REQUEST_EN, 1);
0763 
0764     hws->funcs.hubp_pg_control(hws, 0, false);
0765     REG_SET(DC_IP_REQUEST_CNTL, 0,
0766             IP_REQUEST_EN, 0);
0767 
0768     hws->wa_state.DEGVIDCN10_253_applied = false;
0769 }
0770 
0771 static void apply_DEGVIDCN10_253_wa(struct dc *dc)
0772 {
0773     struct dce_hwseq *hws = dc->hwseq;
0774     struct hubp *hubp = dc->res_pool->hubps[0];
0775     int i;
0776 
0777     if (dc->debug.disable_stutter)
0778         return;
0779 
0780     if (!hws->wa.DEGVIDCN10_253)
0781         return;
0782 
0783     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0784         if (!dc->res_pool->hubps[i]->power_gated)
0785             return;
0786     }
0787 
0788     /* all pipe power gated, apply work around to enable stutter. */
0789 
0790     REG_SET(DC_IP_REQUEST_CNTL, 0,
0791             IP_REQUEST_EN, 1);
0792 
0793     hws->funcs.hubp_pg_control(hws, 0, true);
0794     REG_SET(DC_IP_REQUEST_CNTL, 0,
0795             IP_REQUEST_EN, 0);
0796 
0797     hubp->funcs->set_hubp_blank_en(hubp, false);
0798     hws->wa_state.DEGVIDCN10_253_applied = true;
0799 }
0800 
0801 void dcn10_bios_golden_init(struct dc *dc)
0802 {
0803     struct dce_hwseq *hws = dc->hwseq;
0804     struct dc_bios *bp = dc->ctx->dc_bios;
0805     int i;
0806     bool allow_self_fresh_force_enable = true;
0807 
0808     if (hws->funcs.s0i3_golden_init_wa && hws->funcs.s0i3_golden_init_wa(dc))
0809         return;
0810 
0811     if (dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled)
0812         allow_self_fresh_force_enable =
0813                 dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub);
0814 
0815 
0816     /* WA for making DF sleep when idle after resume from S0i3.
0817      * DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE is set to 1 by
0818      * command table, if DCHUBBUB_ARB_ALLOW_SELF_REFRESH_FORCE_ENABLE = 0
0819      * before calling command table and it changed to 1 after,
0820      * it should be set back to 0.
0821      */
0822 
0823     /* initialize dcn global */
0824     bp->funcs->enable_disp_power_gating(bp,
0825             CONTROLLER_ID_D0, ASIC_PIPE_INIT);
0826 
0827     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0828         /* initialize dcn per pipe */
0829         bp->funcs->enable_disp_power_gating(bp,
0830                 CONTROLLER_ID_D0 + i, ASIC_PIPE_DISABLE);
0831     }
0832 
0833     if (dc->res_pool->hubbub->funcs->allow_self_refresh_control)
0834         if (allow_self_fresh_force_enable == false &&
0835                 dc->res_pool->hubbub->funcs->is_allow_self_refresh_enabled(dc->res_pool->hubbub))
0836             dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
0837                                         !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
0838 
0839 }
0840 
0841 static void false_optc_underflow_wa(
0842         struct dc *dc,
0843         const struct dc_stream_state *stream,
0844         struct timing_generator *tg)
0845 {
0846     int i;
0847     bool underflow;
0848 
0849     if (!dc->hwseq->wa.false_optc_underflow)
0850         return;
0851 
0852     underflow = tg->funcs->is_optc_underflow_occurred(tg);
0853 
0854     for (i = 0; i < dc->res_pool->pipe_count; i++) {
0855         struct pipe_ctx *old_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
0856 
0857         if (old_pipe_ctx->stream != stream)
0858             continue;
0859 
0860         dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, old_pipe_ctx);
0861     }
0862 
0863     if (tg->funcs->set_blank_data_double_buffer)
0864         tg->funcs->set_blank_data_double_buffer(tg, true);
0865 
0866     if (tg->funcs->is_optc_underflow_occurred(tg) && !underflow)
0867         tg->funcs->clear_optc_underflow(tg);
0868 }
0869 
0870 enum dc_status dcn10_enable_stream_timing(
0871         struct pipe_ctx *pipe_ctx,
0872         struct dc_state *context,
0873         struct dc *dc)
0874 {
0875     struct dc_stream_state *stream = pipe_ctx->stream;
0876     enum dc_color_space color_space;
0877     struct tg_color black_color = {0};
0878 
0879     /* by upper caller loop, pipe0 is parent pipe and be called first.
0880      * back end is set up by for pipe0. Other children pipe share back end
0881      * with pipe 0. No program is needed.
0882      */
0883     if (pipe_ctx->top_pipe != NULL)
0884         return DC_OK;
0885 
0886     /* TODO check if timing_changed, disable stream if timing changed */
0887 
0888     /* HW program guide assume display already disable
0889      * by unplug sequence. OTG assume stop.
0890      */
0891     pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, true);
0892 
0893     if (false == pipe_ctx->clock_source->funcs->program_pix_clk(
0894             pipe_ctx->clock_source,
0895             &pipe_ctx->stream_res.pix_clk_params,
0896             dp_get_link_encoding_format(&pipe_ctx->link_config.dp_link_settings),
0897             &pipe_ctx->pll_settings)) {
0898         BREAK_TO_DEBUGGER();
0899         return DC_ERROR_UNEXPECTED;
0900     }
0901 
0902     pipe_ctx->stream_res.tg->funcs->program_timing(
0903             pipe_ctx->stream_res.tg,
0904             &stream->timing,
0905             pipe_ctx->pipe_dlg_param.vready_offset,
0906             pipe_ctx->pipe_dlg_param.vstartup_start,
0907             pipe_ctx->pipe_dlg_param.vupdate_offset,
0908             pipe_ctx->pipe_dlg_param.vupdate_width,
0909             pipe_ctx->stream->signal,
0910             true);
0911 
0912 #if 0 /* move to after enable_crtc */
0913     /* TODO: OPP FMT, ABM. etc. should be done here. */
0914     /* or FPGA now. instance 0 only. TODO: move to opp.c */
0915 
0916     inst_offset = reg_offsets[pipe_ctx->stream_res.tg->inst].fmt;
0917 
0918     pipe_ctx->stream_res.opp->funcs->opp_program_fmt(
0919                 pipe_ctx->stream_res.opp,
0920                 &stream->bit_depth_params,
0921                 &stream->clamping);
0922 #endif
0923     /* program otg blank color */
0924     color_space = stream->output_color_space;
0925     color_space_to_black_color(dc, color_space, &black_color);
0926 
0927     /*
0928      * The way 420 is packed, 2 channels carry Y component, 1 channel
0929      * alternate between Cb and Cr, so both channels need the pixel
0930      * value for Y
0931      */
0932     if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)
0933         black_color.color_r_cr = black_color.color_g_y;
0934 
0935     if (pipe_ctx->stream_res.tg->funcs->set_blank_color)
0936         pipe_ctx->stream_res.tg->funcs->set_blank_color(
0937                 pipe_ctx->stream_res.tg,
0938                 &black_color);
0939 
0940     if (pipe_ctx->stream_res.tg->funcs->is_blanked &&
0941             !pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) {
0942         pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true);
0943         hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg);
0944         false_optc_underflow_wa(dc, pipe_ctx->stream, pipe_ctx->stream_res.tg);
0945     }
0946 
0947     /* VTG is  within DCHUB command block. DCFCLK is always on */
0948     if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) {
0949         BREAK_TO_DEBUGGER();
0950         return DC_ERROR_UNEXPECTED;
0951     }
0952 
0953     /* TODO program crtc source select for non-virtual signal*/
0954     /* TODO program FMT */
0955     /* TODO setup link_enc */
0956     /* TODO set stream attributes */
0957     /* TODO program audio */
0958     /* TODO enable stream if timing changed */
0959     /* TODO unblank stream if DP */
0960 
0961     return DC_OK;
0962 }
0963 
0964 static void dcn10_reset_back_end_for_pipe(
0965         struct dc *dc,
0966         struct pipe_ctx *pipe_ctx,
0967         struct dc_state *context)
0968 {
0969     int i;
0970     struct dc_link *link;
0971     DC_LOGGER_INIT(dc->ctx->logger);
0972     if (pipe_ctx->stream_res.stream_enc == NULL) {
0973         pipe_ctx->stream = NULL;
0974         return;
0975     }
0976 
0977     if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
0978         link = pipe_ctx->stream->link;
0979         /* DPMS may already disable or */
0980         /* dpms_off status is incorrect due to fastboot
0981          * feature. When system resume from S4 with second
0982          * screen only, the dpms_off would be true but
0983          * VBIOS lit up eDP, so check link status too.
0984          */
0985         if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
0986             core_link_disable_stream(pipe_ctx);
0987         else if (pipe_ctx->stream_res.audio)
0988             dc->hwss.disable_audio_stream(pipe_ctx);
0989 
0990         if (pipe_ctx->stream_res.audio) {
0991             /*disable az_endpoint*/
0992             pipe_ctx->stream_res.audio->funcs->az_disable(pipe_ctx->stream_res.audio);
0993 
0994             /*free audio*/
0995             if (dc->caps.dynamic_audio == true) {
0996                 /*we have to dynamic arbitrate the audio endpoints*/
0997                 /*we free the resource, need reset is_audio_acquired*/
0998                 update_audio_usage(&dc->current_state->res_ctx, dc->res_pool,
0999                         pipe_ctx->stream_res.audio, false);
1000                 pipe_ctx->stream_res.audio = NULL;
1001             }
1002         }
1003     }
1004 
1005     /* by upper caller loop, parent pipe: pipe0, will be reset last.
1006      * back end share by all pipes and will be disable only when disable
1007      * parent pipe.
1008      */
1009     if (pipe_ctx->top_pipe == NULL) {
1010 
1011         if (pipe_ctx->stream_res.abm)
1012             dc->hwss.set_abm_immediate_disable(pipe_ctx);
1013 
1014         pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
1015 
1016         pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
1017         if (pipe_ctx->stream_res.tg->funcs->set_drr)
1018             pipe_ctx->stream_res.tg->funcs->set_drr(
1019                     pipe_ctx->stream_res.tg, NULL);
1020     }
1021 
1022     for (i = 0; i < dc->res_pool->pipe_count; i++)
1023         if (&dc->current_state->res_ctx.pipe_ctx[i] == pipe_ctx)
1024             break;
1025 
1026     if (i == dc->res_pool->pipe_count)
1027         return;
1028 
1029     pipe_ctx->stream = NULL;
1030     DC_LOG_DEBUG("Reset back end for pipe %d, tg:%d\n",
1031                     pipe_ctx->pipe_idx, pipe_ctx->stream_res.tg->inst);
1032 }
1033 
1034 static bool dcn10_hw_wa_force_recovery(struct dc *dc)
1035 {
1036     struct hubp *hubp ;
1037     unsigned int i;
1038     bool need_recover = true;
1039 
1040     if (!dc->debug.recovery_enabled)
1041         return false;
1042 
1043     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1044         struct pipe_ctx *pipe_ctx =
1045             &dc->current_state->res_ctx.pipe_ctx[i];
1046         if (pipe_ctx != NULL) {
1047             hubp = pipe_ctx->plane_res.hubp;
1048             if (hubp != NULL && hubp->funcs->hubp_get_underflow_status) {
1049                 if (hubp->funcs->hubp_get_underflow_status(hubp) != 0) {
1050                     /* one pipe underflow, we will reset all the pipes*/
1051                     need_recover = true;
1052                 }
1053             }
1054         }
1055     }
1056     if (!need_recover)
1057         return false;
1058     /*
1059     DCHUBP_CNTL:HUBP_BLANK_EN=1
1060     DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=1
1061     DCHUBP_CNTL:HUBP_DISABLE=1
1062     DCHUBP_CNTL:HUBP_DISABLE=0
1063     DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=0
1064     DCSURF_PRIMARY_SURFACE_ADDRESS
1065     DCHUBP_CNTL:HUBP_BLANK_EN=0
1066     */
1067 
1068     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1069         struct pipe_ctx *pipe_ctx =
1070             &dc->current_state->res_ctx.pipe_ctx[i];
1071         if (pipe_ctx != NULL) {
1072             hubp = pipe_ctx->plane_res.hubp;
1073             /*DCHUBP_CNTL:HUBP_BLANK_EN=1*/
1074             if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
1075                 hubp->funcs->set_hubp_blank_en(hubp, true);
1076         }
1077     }
1078     /*DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=1*/
1079     hubbub1_soft_reset(dc->res_pool->hubbub, true);
1080 
1081     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1082         struct pipe_ctx *pipe_ctx =
1083             &dc->current_state->res_ctx.pipe_ctx[i];
1084         if (pipe_ctx != NULL) {
1085             hubp = pipe_ctx->plane_res.hubp;
1086             /*DCHUBP_CNTL:HUBP_DISABLE=1*/
1087             if (hubp != NULL && hubp->funcs->hubp_disable_control)
1088                 hubp->funcs->hubp_disable_control(hubp, true);
1089         }
1090     }
1091     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1092         struct pipe_ctx *pipe_ctx =
1093             &dc->current_state->res_ctx.pipe_ctx[i];
1094         if (pipe_ctx != NULL) {
1095             hubp = pipe_ctx->plane_res.hubp;
1096             /*DCHUBP_CNTL:HUBP_DISABLE=0*/
1097             if (hubp != NULL && hubp->funcs->hubp_disable_control)
1098                 hubp->funcs->hubp_disable_control(hubp, true);
1099         }
1100     }
1101     /*DCHUBBUB_SOFT_RESET:DCHUBBUB_GLOBAL_SOFT_RESET=0*/
1102     hubbub1_soft_reset(dc->res_pool->hubbub, false);
1103     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1104         struct pipe_ctx *pipe_ctx =
1105             &dc->current_state->res_ctx.pipe_ctx[i];
1106         if (pipe_ctx != NULL) {
1107             hubp = pipe_ctx->plane_res.hubp;
1108             /*DCHUBP_CNTL:HUBP_BLANK_EN=0*/
1109             if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
1110                 hubp->funcs->set_hubp_blank_en(hubp, true);
1111         }
1112     }
1113     return true;
1114 
1115 }
1116 
1117 void dcn10_verify_allow_pstate_change_high(struct dc *dc)
1118 {
1119     struct hubbub *hubbub = dc->res_pool->hubbub;
1120     static bool should_log_hw_state; /* prevent hw state log by default */
1121 
1122     if (!hubbub->funcs->verify_allow_pstate_change_high)
1123         return;
1124 
1125     if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub)) {
1126         int i = 0;
1127 
1128         if (should_log_hw_state)
1129             dcn10_log_hw_state(dc, NULL);
1130 
1131         TRACE_DC_PIPE_STATE(pipe_ctx, i, MAX_PIPES);
1132         BREAK_TO_DEBUGGER();
1133         if (dcn10_hw_wa_force_recovery(dc)) {
1134             /*check again*/
1135             if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub))
1136                 BREAK_TO_DEBUGGER();
1137         }
1138     }
1139 }
1140 
1141 /* trigger HW to start disconnect plane from stream on the next vsync */
1142 void dcn10_plane_atomic_disconnect(struct dc *dc, struct pipe_ctx *pipe_ctx)
1143 {
1144     struct dce_hwseq *hws = dc->hwseq;
1145     struct hubp *hubp = pipe_ctx->plane_res.hubp;
1146     int dpp_id = pipe_ctx->plane_res.dpp->inst;
1147     struct mpc *mpc = dc->res_pool->mpc;
1148     struct mpc_tree *mpc_tree_params;
1149     struct mpcc *mpcc_to_remove = NULL;
1150     struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
1151 
1152     mpc_tree_params = &(opp->mpc_tree_params);
1153     mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id);
1154 
1155     /*Already reset*/
1156     if (mpcc_to_remove == NULL)
1157         return;
1158 
1159     mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove);
1160     // Phantom pipes have OTG disabled by default, so MPCC_STATUS will never assert idle,
1161     // so don't wait for MPCC_IDLE in the programming sequence
1162     if (opp != NULL && !pipe_ctx->plane_state->is_phantom)
1163         opp->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true;
1164 
1165     dc->optimized_required = true;
1166 
1167     if (hubp->funcs->hubp_disconnect)
1168         hubp->funcs->hubp_disconnect(hubp);
1169 
1170     if (dc->debug.sanity_checks)
1171         hws->funcs.verify_allow_pstate_change_high(dc);
1172 }
1173 
1174 /**
1175  * dcn10_plane_atomic_power_down - Power down plane components.
1176  *
1177  * @dc: dc struct reference. used for grab hwseq.
1178  * @dpp: dpp struct reference.
1179  * @hubp: hubp struct reference.
1180  *
1181  * Keep in mind that this operation requires a power gate configuration;
1182  * however, requests for switch power gate are precisely controlled to avoid
1183  * problems. For this reason, power gate request is usually disabled. This
1184  * function first needs to enable the power gate request before disabling DPP
1185  * and HUBP. Finally, it disables the power gate request again.
1186  */
1187 void dcn10_plane_atomic_power_down(struct dc *dc,
1188         struct dpp *dpp,
1189         struct hubp *hubp)
1190 {
1191     struct dce_hwseq *hws = dc->hwseq;
1192     DC_LOGGER_INIT(dc->ctx->logger);
1193 
1194     if (REG(DC_IP_REQUEST_CNTL)) {
1195         REG_SET(DC_IP_REQUEST_CNTL, 0,
1196                 IP_REQUEST_EN, 1);
1197 
1198         if (hws->funcs.dpp_pg_control)
1199             hws->funcs.dpp_pg_control(hws, dpp->inst, false);
1200 
1201         if (hws->funcs.hubp_pg_control)
1202             hws->funcs.hubp_pg_control(hws, hubp->inst, false);
1203 
1204         dpp->funcs->dpp_reset(dpp);
1205         REG_SET(DC_IP_REQUEST_CNTL, 0,
1206                 IP_REQUEST_EN, 0);
1207         DC_LOG_DEBUG(
1208                 "Power gated front end %d\n", hubp->inst);
1209     }
1210 }
1211 
1212 /* disable HW used by plane.
1213  * note:  cannot disable until disconnect is complete
1214  */
1215 void dcn10_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx)
1216 {
1217     struct dce_hwseq *hws = dc->hwseq;
1218     struct hubp *hubp = pipe_ctx->plane_res.hubp;
1219     struct dpp *dpp = pipe_ctx->plane_res.dpp;
1220     int opp_id = hubp->opp_id;
1221 
1222     dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx);
1223 
1224     hubp->funcs->hubp_clk_cntl(hubp, false);
1225 
1226     dpp->funcs->dpp_dppclk_control(dpp, false, false);
1227 
1228     if (opp_id != 0xf && pipe_ctx->stream_res.opp->mpc_tree_params.opp_list == NULL)
1229         pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control(
1230                 pipe_ctx->stream_res.opp,
1231                 false);
1232 
1233     hubp->power_gated = true;
1234     dc->optimized_required = false; /* We're powering off, no need to optimize */
1235 
1236     hws->funcs.plane_atomic_power_down(dc,
1237             pipe_ctx->plane_res.dpp,
1238             pipe_ctx->plane_res.hubp);
1239 
1240     pipe_ctx->stream = NULL;
1241     memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res));
1242     memset(&pipe_ctx->plane_res, 0, sizeof(pipe_ctx->plane_res));
1243     pipe_ctx->top_pipe = NULL;
1244     pipe_ctx->bottom_pipe = NULL;
1245     pipe_ctx->plane_state = NULL;
1246 }
1247 
1248 void dcn10_disable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx)
1249 {
1250     struct dce_hwseq *hws = dc->hwseq;
1251     DC_LOGGER_INIT(dc->ctx->logger);
1252 
1253     if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated)
1254         return;
1255 
1256     hws->funcs.plane_atomic_disable(dc, pipe_ctx);
1257 
1258     apply_DEGVIDCN10_253_wa(dc);
1259 
1260     DC_LOG_DC("Power down front end %d\n",
1261                     pipe_ctx->pipe_idx);
1262 }
1263 
1264 void dcn10_init_pipes(struct dc *dc, struct dc_state *context)
1265 {
1266     int i;
1267     struct dce_hwseq *hws = dc->hwseq;
1268     struct hubbub *hubbub = dc->res_pool->hubbub;
1269     bool can_apply_seamless_boot = false;
1270 
1271     for (i = 0; i < context->stream_count; i++) {
1272         if (context->streams[i]->apply_seamless_boot_optimization) {
1273             can_apply_seamless_boot = true;
1274             break;
1275         }
1276     }
1277 
1278     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1279         struct timing_generator *tg = dc->res_pool->timing_generators[i];
1280         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1281 
1282         /* There is assumption that pipe_ctx is not mapping irregularly
1283          * to non-preferred front end. If pipe_ctx->stream is not NULL,
1284          * we will use the pipe, so don't disable
1285          */
1286         if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
1287             continue;
1288 
1289         /* Blank controller using driver code instead of
1290          * command table.
1291          */
1292         if (tg->funcs->is_tg_enabled(tg)) {
1293             if (hws->funcs.init_blank != NULL) {
1294                 hws->funcs.init_blank(dc, tg);
1295                 tg->funcs->lock(tg);
1296             } else {
1297                 tg->funcs->lock(tg);
1298                 tg->funcs->set_blank(tg, true);
1299                 hwss_wait_for_blank_complete(tg);
1300             }
1301         }
1302     }
1303 
1304     /* Reset det size */
1305     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1306         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1307         struct hubp *hubp = dc->res_pool->hubps[i];
1308 
1309         /* Do not need to reset for seamless boot */
1310         if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
1311             continue;
1312 
1313         if (hubbub && hubp) {
1314             if (hubbub->funcs->program_det_size)
1315                 hubbub->funcs->program_det_size(hubbub, hubp->inst, 0);
1316         }
1317     }
1318 
1319     /* num_opp will be equal to number of mpcc */
1320     for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) {
1321         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1322 
1323         /* Cannot reset the MPC mux if seamless boot */
1324         if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
1325             continue;
1326 
1327         dc->res_pool->mpc->funcs->mpc_init_single_inst(
1328                 dc->res_pool->mpc, i);
1329     }
1330 
1331     for (i = 0; i < dc->res_pool->pipe_count; i++) {
1332         struct timing_generator *tg = dc->res_pool->timing_generators[i];
1333         struct hubp *hubp = dc->res_pool->hubps[i];
1334         struct dpp *dpp = dc->res_pool->dpps[i];
1335         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1336 
1337         /* There is assumption that pipe_ctx is not mapping irregularly
1338          * to non-preferred front end. If pipe_ctx->stream is not NULL,
1339          * we will use the pipe, so don't disable
1340          */
1341         if (can_apply_seamless_boot &&
1342             pipe_ctx->stream != NULL &&
1343             pipe_ctx->stream_res.tg->funcs->is_tg_enabled(
1344                 pipe_ctx->stream_res.tg)) {
1345             // Enable double buffering for OTG_BLANK no matter if
1346             // seamless boot is enabled or not to suppress global sync
1347             // signals when OTG blanked. This is to prevent pipe from
1348             // requesting data while in PSR.
1349             tg->funcs->tg_init(tg);
1350             hubp->power_gated = true;
1351             continue;
1352         }
1353 
1354         /* Disable on the current state so the new one isn't cleared. */
1355         pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
1356 
1357         dpp->funcs->dpp_reset(dpp);
1358 
1359         pipe_ctx->stream_res.tg = tg;
1360         pipe_ctx->pipe_idx = i;
1361 
1362         pipe_ctx->plane_res.hubp = hubp;
1363         pipe_ctx->plane_res.dpp = dpp;
1364         pipe_ctx->plane_res.mpcc_inst = dpp->inst;
1365         hubp->mpcc_id = dpp->inst;
1366         hubp->opp_id = OPP_ID_INVALID;
1367         hubp->power_gated = false;
1368 
1369         dc->res_pool->opps[i]->mpc_tree_params.opp_id = dc->res_pool->opps[i]->inst;
1370         dc->res_pool->opps[i]->mpc_tree_params.opp_list = NULL;
1371         dc->res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true;
1372         pipe_ctx->stream_res.opp = dc->res_pool->opps[i];
1373 
1374         hws->funcs.plane_atomic_disconnect(dc, pipe_ctx);
1375 
1376         if (tg->funcs->is_tg_enabled(tg))
1377             tg->funcs->unlock(tg);
1378 
1379         dc->hwss.disable_plane(dc, pipe_ctx);
1380 
1381         pipe_ctx->stream_res.tg = NULL;
1382         pipe_ctx->plane_res.hubp = NULL;
1383 
1384         if (tg->funcs->is_tg_enabled(tg)) {
1385             if (tg->funcs->init_odm)
1386                 tg->funcs->init_odm(tg);
1387         }
1388 
1389         tg->funcs->tg_init(tg);
1390     }
1391 
1392     /* Power gate DSCs */
1393     if (hws->funcs.dsc_pg_control != NULL) {
1394         uint32_t num_opps = 0;
1395         uint32_t opp_id_src0 = OPP_ID_INVALID;
1396         uint32_t opp_id_src1 = OPP_ID_INVALID;
1397 
1398         // Step 1: To find out which OPTC is running & OPTC DSC is ON
1399         // We can't use res_pool->res_cap->num_timing_generator to check
1400         // Because it records display pipes default setting built in driver,
1401         // not display pipes of the current chip.
1402         // Some ASICs would be fused display pipes less than the default setting.
1403         // In dcnxx_resource_construct function, driver would obatin real information.
1404         for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
1405             uint32_t optc_dsc_state = 0;
1406             struct timing_generator *tg = dc->res_pool->timing_generators[i];
1407 
1408             if (tg->funcs->is_tg_enabled(tg)) {
1409                 if (tg->funcs->get_dsc_status)
1410                     tg->funcs->get_dsc_status(tg, &optc_dsc_state);
1411                 // Only one OPTC with DSC is ON, so if we got one result, we would exit this block.
1412                 // non-zero value is DSC enabled
1413                 if (optc_dsc_state != 0) {
1414                     tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1);
1415                     break;
1416                 }
1417             }
1418         }
1419 
1420         // Step 2: To power down DSC but skip DSC  of running OPTC
1421         for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) {
1422             struct dcn_dsc_state s  = {0};
1423 
1424             dc->res_pool->dscs[i]->funcs->dsc_read_state(dc->res_pool->dscs[i], &s);
1425 
1426             if ((s.dsc_opp_source == opp_id_src0 || s.dsc_opp_source == opp_id_src1) &&
1427                 s.dsc_clock_en && s.dsc_fw_en)
1428                 continue;
1429 
1430             hws->funcs.dsc_pg_control(hws, dc->res_pool->dscs[i]->inst, false);
1431         }
1432     }
1433 }
1434 
1435 void dcn10_init_hw(struct dc *dc)
1436 {
1437     int i;
1438     struct abm *abm = dc->res_pool->abm;
1439     struct dmcu *dmcu = dc->res_pool->dmcu;
1440     struct dce_hwseq *hws = dc->hwseq;
1441     struct dc_bios *dcb = dc->ctx->dc_bios;
1442     struct resource_pool *res_pool = dc->res_pool;
1443     uint32_t backlight = MAX_BACKLIGHT_LEVEL;
1444     bool   is_optimized_init_done = false;
1445 
1446     if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks)
1447         dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
1448 
1449     /* Align bw context with hw config when system resume. */
1450     if (dc->clk_mgr->clks.dispclk_khz != 0 && dc->clk_mgr->clks.dppclk_khz != 0) {
1451         dc->current_state->bw_ctx.bw.dcn.clk.dispclk_khz = dc->clk_mgr->clks.dispclk_khz;
1452         dc->current_state->bw_ctx.bw.dcn.clk.dppclk_khz = dc->clk_mgr->clks.dppclk_khz;
1453     }
1454 
1455     // Initialize the dccg
1456     if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->dccg_init)
1457         dc->res_pool->dccg->funcs->dccg_init(res_pool->dccg);
1458 
1459     if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
1460 
1461         REG_WRITE(REFCLK_CNTL, 0);
1462         REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1);
1463         REG_WRITE(DIO_MEM_PWR_CTRL, 0);
1464 
1465         if (!dc->debug.disable_clock_gate) {
1466             /* enable all DCN clock gating */
1467             REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
1468 
1469             REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
1470 
1471             REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
1472         }
1473 
1474         //Enable ability to power gate / don't force power on permanently
1475         if (hws->funcs.enable_power_gating_plane)
1476             hws->funcs.enable_power_gating_plane(hws, true);
1477 
1478         return;
1479     }
1480 
1481     if (!dcb->funcs->is_accelerated_mode(dcb))
1482         hws->funcs.disable_vga(dc->hwseq);
1483 
1484     hws->funcs.bios_golden_init(dc);
1485 
1486     if (dc->ctx->dc_bios->fw_info_valid) {
1487         res_pool->ref_clocks.xtalin_clock_inKhz =
1488                 dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency;
1489 
1490         if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
1491             if (res_pool->dccg && res_pool->hubbub) {
1492 
1493                 (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg,
1494                         dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency,
1495                         &res_pool->ref_clocks.dccg_ref_clock_inKhz);
1496 
1497                 (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub,
1498                         res_pool->ref_clocks.dccg_ref_clock_inKhz,
1499                         &res_pool->ref_clocks.dchub_ref_clock_inKhz);
1500             } else {
1501                 // Not all ASICs have DCCG sw component
1502                 res_pool->ref_clocks.dccg_ref_clock_inKhz =
1503                         res_pool->ref_clocks.xtalin_clock_inKhz;
1504                 res_pool->ref_clocks.dchub_ref_clock_inKhz =
1505                         res_pool->ref_clocks.xtalin_clock_inKhz;
1506             }
1507         }
1508     } else
1509         ASSERT_CRITICAL(false);
1510 
1511     for (i = 0; i < dc->link_count; i++) {
1512         /* Power up AND update implementation according to the
1513          * required signal (which may be different from the
1514          * default signal on connector).
1515          */
1516         struct dc_link *link = dc->links[i];
1517 
1518         if (!is_optimized_init_done)
1519             link->link_enc->funcs->hw_init(link->link_enc);
1520 
1521         /* Check for enabled DIG to identify enabled display */
1522         if (link->link_enc->funcs->is_dig_enabled &&
1523             link->link_enc->funcs->is_dig_enabled(link->link_enc)) {
1524             link->link_status.link_active = true;
1525             if (link->link_enc->funcs->fec_is_active &&
1526                     link->link_enc->funcs->fec_is_active(link->link_enc))
1527                 link->fec_state = dc_link_fec_enabled;
1528         }
1529     }
1530 
1531     /* we want to turn off all dp displays before doing detection */
1532     dc_link_blank_all_dp_displays(dc);
1533 
1534     if (hws->funcs.enable_power_gating_plane)
1535         hws->funcs.enable_power_gating_plane(dc->hwseq, true);
1536 
1537     /* If taking control over from VBIOS, we may want to optimize our first
1538      * mode set, so we need to skip powering down pipes until we know which
1539      * pipes we want to use.
1540      * Otherwise, if taking control is not possible, we need to power
1541      * everything down.
1542      */
1543     if (dcb->funcs->is_accelerated_mode(dcb) || !dc->config.seamless_boot_edp_requested) {
1544         if (!is_optimized_init_done) {
1545             hws->funcs.init_pipes(dc, dc->current_state);
1546             if (dc->res_pool->hubbub->funcs->allow_self_refresh_control)
1547                 dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
1548                         !dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
1549         }
1550     }
1551 
1552     if (!is_optimized_init_done) {
1553 
1554         for (i = 0; i < res_pool->audio_count; i++) {
1555             struct audio *audio = res_pool->audios[i];
1556 
1557             audio->funcs->hw_init(audio);
1558         }
1559 
1560         for (i = 0; i < dc->link_count; i++) {
1561             struct dc_link *link = dc->links[i];
1562 
1563             if (link->panel_cntl)
1564                 backlight = link->panel_cntl->funcs->hw_init(link->panel_cntl);
1565         }
1566 
1567         if (abm != NULL)
1568             abm->funcs->abm_init(abm, backlight);
1569 
1570         if (dmcu != NULL && !dmcu->auto_load_dmcu)
1571             dmcu->funcs->dmcu_init(dmcu);
1572     }
1573 
1574     if (abm != NULL && dmcu != NULL)
1575         abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
1576 
1577     /* power AFMT HDMI memory TODO: may move to dis/en output save power*/
1578     if (!is_optimized_init_done)
1579         REG_WRITE(DIO_MEM_PWR_CTRL, 0);
1580 
1581     if (!dc->debug.disable_clock_gate) {
1582         /* enable all DCN clock gating */
1583         REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
1584 
1585         REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0);
1586 
1587         REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
1588     }
1589 
1590     if (dc->clk_mgr->funcs->notify_wm_ranges)
1591         dc->clk_mgr->funcs->notify_wm_ranges(dc->clk_mgr);
1592 }
1593 
1594 /* In headless boot cases, DIG may be turned
1595  * on which causes HW/SW discrepancies.
1596  * To avoid this, power down hardware on boot
1597  * if DIG is turned on
1598  */
1599 void dcn10_power_down_on_boot(struct dc *dc)
1600 {
1601     struct dc_link *edp_links[MAX_NUM_EDP];
1602     struct dc_link *edp_link = NULL;
1603     int edp_num;
1604     int i = 0;
1605 
1606     get_edp_links(dc, edp_links, &edp_num);
1607     if (edp_num)
1608         edp_link = edp_links[0];
1609 
1610     if (edp_link && edp_link->link_enc->funcs->is_dig_enabled &&
1611             edp_link->link_enc->funcs->is_dig_enabled(edp_link->link_enc) &&
1612             dc->hwseq->funcs.edp_backlight_control &&
1613             dc->hwss.power_down &&
1614             dc->hwss.edp_power_control) {
1615         dc->hwseq->funcs.edp_backlight_control(edp_link, false);
1616         dc->hwss.power_down(dc);
1617         dc->hwss.edp_power_control(edp_link, false);
1618     } else {
1619         for (i = 0; i < dc->link_count; i++) {
1620             struct dc_link *link = dc->links[i];
1621 
1622             if (link->link_enc && link->link_enc->funcs->is_dig_enabled &&
1623                     link->link_enc->funcs->is_dig_enabled(link->link_enc) &&
1624                     dc->hwss.power_down) {
1625                 dc->hwss.power_down(dc);
1626                 break;
1627             }
1628 
1629         }
1630     }
1631 
1632     /*
1633      * Call update_clocks with empty context
1634      * to send DISPLAY_OFF
1635      * Otherwise DISPLAY_OFF may not be asserted
1636      */
1637     if (dc->clk_mgr->funcs->set_low_power_state)
1638         dc->clk_mgr->funcs->set_low_power_state(dc->clk_mgr);
1639 }
1640 
1641 void dcn10_reset_hw_ctx_wrap(
1642         struct dc *dc,
1643         struct dc_state *context)
1644 {
1645     int i;
1646     struct dce_hwseq *hws = dc->hwseq;
1647 
1648     /* Reset Back End*/
1649     for (i = dc->res_pool->pipe_count - 1; i >= 0 ; i--) {
1650         struct pipe_ctx *pipe_ctx_old =
1651             &dc->current_state->res_ctx.pipe_ctx[i];
1652         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
1653 
1654         if (!pipe_ctx_old->stream)
1655             continue;
1656 
1657         if (pipe_ctx_old->top_pipe)
1658             continue;
1659 
1660         if (!pipe_ctx->stream ||
1661                 pipe_need_reprogram(pipe_ctx_old, pipe_ctx)) {
1662             struct clock_source *old_clk = pipe_ctx_old->clock_source;
1663 
1664             dcn10_reset_back_end_for_pipe(dc, pipe_ctx_old, dc->current_state);
1665             if (hws->funcs.enable_stream_gating)
1666                 hws->funcs.enable_stream_gating(dc, pipe_ctx_old);
1667             if (old_clk)
1668                 old_clk->funcs->cs_power_down(old_clk);
1669         }
1670     }
1671 }
1672 
1673 static bool patch_address_for_sbs_tb_stereo(
1674         struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr)
1675 {
1676     struct dc_plane_state *plane_state = pipe_ctx->plane_state;
1677     bool sec_split = pipe_ctx->top_pipe &&
1678             pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state;
1679     if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO &&
1680         (pipe_ctx->stream->timing.timing_3d_format ==
1681          TIMING_3D_FORMAT_SIDE_BY_SIDE ||
1682          pipe_ctx->stream->timing.timing_3d_format ==
1683          TIMING_3D_FORMAT_TOP_AND_BOTTOM)) {
1684         *addr = plane_state->address.grph_stereo.left_addr;
1685         plane_state->address.grph_stereo.left_addr =
1686         plane_state->address.grph_stereo.right_addr;
1687         return true;
1688     } else {
1689         if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE &&
1690             plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) {
1691             plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO;
1692             plane_state->address.grph_stereo.right_addr =
1693             plane_state->address.grph_stereo.left_addr;
1694             plane_state->address.grph_stereo.right_meta_addr =
1695             plane_state->address.grph_stereo.left_meta_addr;
1696         }
1697     }
1698     return false;
1699 }
1700 
1701 void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx)
1702 {
1703     bool addr_patched = false;
1704     PHYSICAL_ADDRESS_LOC addr;
1705     struct dc_plane_state *plane_state = pipe_ctx->plane_state;
1706 
1707     if (plane_state == NULL)
1708         return;
1709 
1710     addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr);
1711 
1712     pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr(
1713             pipe_ctx->plane_res.hubp,
1714             &plane_state->address,
1715             plane_state->flip_immediate);
1716 
1717     plane_state->status.requested_address = plane_state->address;
1718 
1719     if (plane_state->flip_immediate)
1720         plane_state->status.current_address = plane_state->address;
1721 
1722     if (addr_patched)
1723         pipe_ctx->plane_state->address.grph_stereo.left_addr = addr;
1724 }
1725 
1726 bool dcn10_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx,
1727             const struct dc_plane_state *plane_state)
1728 {
1729     struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
1730     const struct dc_transfer_func *tf = NULL;
1731     bool result = true;
1732 
1733     if (dpp_base == NULL)
1734         return false;
1735 
1736     if (plane_state->in_transfer_func)
1737         tf = plane_state->in_transfer_func;
1738 
1739     if (plane_state->gamma_correction &&
1740         !dpp_base->ctx->dc->debug.always_use_regamma
1741         && !plane_state->gamma_correction->is_identity
1742             && dce_use_lut(plane_state->format))
1743         dpp_base->funcs->dpp_program_input_lut(dpp_base, plane_state->gamma_correction);
1744 
1745     if (tf == NULL)
1746         dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS);
1747     else if (tf->type == TF_TYPE_PREDEFINED) {
1748         switch (tf->tf) {
1749         case TRANSFER_FUNCTION_SRGB:
1750             dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_HW_sRGB);
1751             break;
1752         case TRANSFER_FUNCTION_BT709:
1753             dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_HW_xvYCC);
1754             break;
1755         case TRANSFER_FUNCTION_LINEAR:
1756             dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS);
1757             break;
1758         case TRANSFER_FUNCTION_PQ:
1759             dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_USER_PWL);
1760             cm_helper_translate_curve_to_degamma_hw_format(tf, &dpp_base->degamma_params);
1761             dpp_base->funcs->dpp_program_degamma_pwl(dpp_base, &dpp_base->degamma_params);
1762             result = true;
1763             break;
1764         default:
1765             result = false;
1766             break;
1767         }
1768     } else if (tf->type == TF_TYPE_BYPASS) {
1769         dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS);
1770     } else {
1771         cm_helper_translate_curve_to_degamma_hw_format(tf,
1772                     &dpp_base->degamma_params);
1773         dpp_base->funcs->dpp_program_degamma_pwl(dpp_base,
1774                 &dpp_base->degamma_params);
1775         result = true;
1776     }
1777 
1778     return result;
1779 }
1780 
1781 #define MAX_NUM_HW_POINTS 0x200
1782 
1783 static void log_tf(struct dc_context *ctx,
1784                 struct dc_transfer_func *tf, uint32_t hw_points_num)
1785 {
1786     // DC_LOG_GAMMA is default logging of all hw points
1787     // DC_LOG_ALL_GAMMA logs all points, not only hw points
1788     // DC_LOG_ALL_TF_POINTS logs all channels of the tf
1789     int i = 0;
1790 
1791     DC_LOGGER_INIT(ctx->logger);
1792     DC_LOG_GAMMA("Gamma Correction TF");
1793     DC_LOG_ALL_GAMMA("Logging all tf points...");
1794     DC_LOG_ALL_TF_CHANNELS("Logging all channels...");
1795 
1796     for (i = 0; i < hw_points_num; i++) {
1797         DC_LOG_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value);
1798         DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value);
1799         DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value);
1800     }
1801 
1802     for (i = hw_points_num; i < MAX_NUM_HW_POINTS; i++) {
1803         DC_LOG_ALL_GAMMA("R\t%d\t%llu", i, tf->tf_pts.red[i].value);
1804         DC_LOG_ALL_TF_CHANNELS("G\t%d\t%llu", i, tf->tf_pts.green[i].value);
1805         DC_LOG_ALL_TF_CHANNELS("B\t%d\t%llu", i, tf->tf_pts.blue[i].value);
1806     }
1807 }
1808 
1809 bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx,
1810                 const struct dc_stream_state *stream)
1811 {
1812     struct dpp *dpp = pipe_ctx->plane_res.dpp;
1813 
1814     if (dpp == NULL)
1815         return false;
1816 
1817     dpp->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM;
1818 
1819     if (stream->out_transfer_func &&
1820         stream->out_transfer_func->type == TF_TYPE_PREDEFINED &&
1821         stream->out_transfer_func->tf == TRANSFER_FUNCTION_SRGB)
1822         dpp->funcs->dpp_program_regamma_pwl(dpp, NULL, OPP_REGAMMA_SRGB);
1823 
1824     /* dcn10_translate_regamma_to_hw_format takes 750us, only do it when full
1825      * update.
1826      */
1827     else if (cm_helper_translate_curve_to_hw_format(
1828             stream->out_transfer_func,
1829             &dpp->regamma_params, false)) {
1830         dpp->funcs->dpp_program_regamma_pwl(
1831                 dpp,
1832                 &dpp->regamma_params, OPP_REGAMMA_USER);
1833     } else
1834         dpp->funcs->dpp_program_regamma_pwl(dpp, NULL, OPP_REGAMMA_BYPASS);
1835 
1836     if (stream != NULL && stream->ctx != NULL &&
1837             stream->out_transfer_func != NULL) {
1838         log_tf(stream->ctx,
1839                 stream->out_transfer_func,
1840                 dpp->regamma_params.hw_points_num);
1841     }
1842 
1843     return true;
1844 }
1845 
1846 void dcn10_pipe_control_lock(
1847     struct dc *dc,
1848     struct pipe_ctx *pipe,
1849     bool lock)
1850 {
1851     struct dce_hwseq *hws = dc->hwseq;
1852 
1853     /* use TG master update lock to lock everything on the TG
1854      * therefore only top pipe need to lock
1855      */
1856     if (!pipe || pipe->top_pipe)
1857         return;
1858 
1859     if (dc->debug.sanity_checks)
1860         hws->funcs.verify_allow_pstate_change_high(dc);
1861 
1862     if (lock)
1863         pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg);
1864     else
1865         pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg);
1866 
1867     if (dc->debug.sanity_checks)
1868         hws->funcs.verify_allow_pstate_change_high(dc);
1869 }
1870 
1871 /**
1872  * delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE.
1873  *
1874  * Software keepout workaround to prevent cursor update locking from stalling
1875  * out cursor updates indefinitely or from old values from being retained in
1876  * the case where the viewport changes in the same frame as the cursor.
1877  *
1878  * The idea is to calculate the remaining time from VPOS to VUPDATE. If it's
1879  * too close to VUPDATE, then stall out until VUPDATE finishes.
1880  *
1881  * TODO: Optimize cursor programming to be once per frame before VUPDATE
1882  *       to avoid the need for this workaround.
1883  */
1884 static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx)
1885 {
1886     struct dc_stream_state *stream = pipe_ctx->stream;
1887     struct crtc_position position;
1888     uint32_t vupdate_start, vupdate_end;
1889     unsigned int lines_to_vupdate, us_to_vupdate, vpos;
1890     unsigned int us_per_line, us_vupdate;
1891 
1892     if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position)
1893         return;
1894 
1895     if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg)
1896         return;
1897 
1898     dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start,
1899                        &vupdate_end);
1900 
1901     dc->hwss.get_position(&pipe_ctx, 1, &position);
1902     vpos = position.vertical_count;
1903 
1904     /* Avoid wraparound calculation issues */
1905     vupdate_start += stream->timing.v_total;
1906     vupdate_end += stream->timing.v_total;
1907     vpos += stream->timing.v_total;
1908 
1909     if (vpos <= vupdate_start) {
1910         /* VPOS is in VACTIVE or back porch. */
1911         lines_to_vupdate = vupdate_start - vpos;
1912     } else if (vpos > vupdate_end) {
1913         /* VPOS is in the front porch. */
1914         return;
1915     } else {
1916         /* VPOS is in VUPDATE. */
1917         lines_to_vupdate = 0;
1918     }
1919 
1920     /* Calculate time until VUPDATE in microseconds. */
1921     us_per_line =
1922         stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz;
1923     us_to_vupdate = lines_to_vupdate * us_per_line;
1924 
1925     /* 70 us is a conservative estimate of cursor update time*/
1926     if (us_to_vupdate > 70)
1927         return;
1928 
1929     /* Stall out until the cursor update completes. */
1930     if (vupdate_end < vupdate_start)
1931         vupdate_end += stream->timing.v_total;
1932     us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line;
1933     udelay(us_to_vupdate + us_vupdate);
1934 }
1935 
1936 void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock)
1937 {
1938     /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */
1939     if (!pipe || pipe->top_pipe)
1940         return;
1941 
1942     /* Prevent cursor lock from stalling out cursor updates. */
1943     if (lock)
1944         delay_cursor_until_vupdate(dc, pipe);
1945 
1946     if (pipe->stream && should_use_dmub_lock(pipe->stream->link)) {
1947         union dmub_hw_lock_flags hw_locks = { 0 };
1948         struct dmub_hw_lock_inst_flags inst_flags = { 0 };
1949 
1950         hw_locks.bits.lock_cursor = 1;
1951         inst_flags.opp_inst = pipe->stream_res.opp->inst;
1952 
1953         dmub_hw_lock_mgr_cmd(dc->ctx->dmub_srv,
1954                     lock,
1955                     &hw_locks,
1956                     &inst_flags);
1957     } else
1958         dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
1959                 pipe->stream_res.opp->inst, lock);
1960 }
1961 
1962 static bool wait_for_reset_trigger_to_occur(
1963     struct dc_context *dc_ctx,
1964     struct timing_generator *tg)
1965 {
1966     bool rc = false;
1967 
1968     /* To avoid endless loop we wait at most
1969      * frames_to_wait_on_triggered_reset frames for the reset to occur. */
1970     const uint32_t frames_to_wait_on_triggered_reset = 10;
1971     int i;
1972 
1973     for (i = 0; i < frames_to_wait_on_triggered_reset; i++) {
1974 
1975         if (!tg->funcs->is_counter_moving(tg)) {
1976             DC_ERROR("TG counter is not moving!\n");
1977             break;
1978         }
1979 
1980         if (tg->funcs->did_triggered_reset_occur(tg)) {
1981             rc = true;
1982             /* usually occurs at i=1 */
1983             DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n",
1984                     i);
1985             break;
1986         }
1987 
1988         /* Wait for one frame. */
1989         tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE);
1990         tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK);
1991     }
1992 
1993     if (false == rc)
1994         DC_ERROR("GSL: Timeout on reset trigger!\n");
1995 
1996     return rc;
1997 }
1998 
1999 static uint64_t reduceSizeAndFraction(uint64_t *numerator,
2000                       uint64_t *denominator,
2001                       bool checkUint32Bounary)
2002 {
2003     int i;
2004     bool ret = checkUint32Bounary == false;
2005     uint64_t max_int32 = 0xffffffff;
2006     uint64_t num, denom;
2007     static const uint16_t prime_numbers[] = {
2008         2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
2009         47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
2010         107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
2011         167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
2012         229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
2013         283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353,
2014         359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,
2015         431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487,
2016         491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
2017         571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631,
2018         641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
2019         709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773,
2020         787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857,
2021         859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
2022         941, 947, 953, 967, 971, 977, 983, 991, 997};
2023     int count = ARRAY_SIZE(prime_numbers);
2024 
2025     num = *numerator;
2026     denom = *denominator;
2027     for (i = 0; i < count; i++) {
2028         uint32_t num_remainder, denom_remainder;
2029         uint64_t num_result, denom_result;
2030         if (checkUint32Bounary &&
2031             num <= max_int32 && denom <= max_int32) {
2032             ret = true;
2033             break;
2034         }
2035         do {
2036             num_result = div_u64_rem(num, prime_numbers[i], &num_remainder);
2037             denom_result = div_u64_rem(denom, prime_numbers[i], &denom_remainder);
2038             if (num_remainder == 0 && denom_remainder == 0) {
2039                 num = num_result;
2040                 denom = denom_result;
2041             }
2042         } while (num_remainder == 0 && denom_remainder == 0);
2043     }
2044     *numerator = num;
2045     *denominator = denom;
2046     return ret;
2047 }
2048 
2049 static bool is_low_refresh_rate(struct pipe_ctx *pipe)
2050 {
2051     uint32_t master_pipe_refresh_rate =
2052         pipe->stream->timing.pix_clk_100hz * 100 /
2053         pipe->stream->timing.h_total /
2054         pipe->stream->timing.v_total;
2055     return master_pipe_refresh_rate <= 30;
2056 }
2057 
2058 static uint8_t get_clock_divider(struct pipe_ctx *pipe,
2059                  bool account_low_refresh_rate)
2060 {
2061     uint32_t clock_divider = 1;
2062     uint32_t numpipes = 1;
2063 
2064     if (account_low_refresh_rate && is_low_refresh_rate(pipe))
2065         clock_divider *= 2;
2066 
2067     if (pipe->stream_res.pix_clk_params.pixel_encoding == PIXEL_ENCODING_YCBCR420)
2068         clock_divider *= 2;
2069 
2070     while (pipe->next_odm_pipe) {
2071         pipe = pipe->next_odm_pipe;
2072         numpipes++;
2073     }
2074     clock_divider *= numpipes;
2075 
2076     return clock_divider;
2077 }
2078 
2079 static int dcn10_align_pixel_clocks(struct dc *dc, int group_size,
2080                     struct pipe_ctx *grouped_pipes[])
2081 {
2082     struct dc_context *dc_ctx = dc->ctx;
2083     int i, master = -1, embedded = -1;
2084     struct dc_crtc_timing *hw_crtc_timing;
2085     uint64_t phase[MAX_PIPES];
2086     uint64_t modulo[MAX_PIPES];
2087     unsigned int pclk;
2088 
2089     uint32_t embedded_pix_clk_100hz;
2090     uint16_t embedded_h_total;
2091     uint16_t embedded_v_total;
2092     uint32_t dp_ref_clk_100hz =
2093         dc->res_pool->dp_clock_source->ctx->dc->clk_mgr->dprefclk_khz*10;
2094 
2095     hw_crtc_timing = kcalloc(MAX_PIPES, sizeof(*hw_crtc_timing), GFP_KERNEL);
2096     if (!hw_crtc_timing)
2097         return master;
2098 
2099     if (dc->config.vblank_alignment_dto_params &&
2100         dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk) {
2101         embedded_h_total =
2102             (dc->config.vblank_alignment_dto_params >> 32) & 0x7FFF;
2103         embedded_v_total =
2104             (dc->config.vblank_alignment_dto_params >> 48) & 0x7FFF;
2105         embedded_pix_clk_100hz =
2106             dc->config.vblank_alignment_dto_params & 0xFFFFFFFF;
2107 
2108         for (i = 0; i < group_size; i++) {
2109             grouped_pipes[i]->stream_res.tg->funcs->get_hw_timing(
2110                     grouped_pipes[i]->stream_res.tg,
2111                     &hw_crtc_timing[i]);
2112             dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
2113                 dc->res_pool->dp_clock_source,
2114                 grouped_pipes[i]->stream_res.tg->inst,
2115                 &pclk);
2116             hw_crtc_timing[i].pix_clk_100hz = pclk;
2117             if (dc_is_embedded_signal(
2118                     grouped_pipes[i]->stream->signal)) {
2119                 embedded = i;
2120                 master = i;
2121                 phase[i] = embedded_pix_clk_100hz*100;
2122                 modulo[i] = dp_ref_clk_100hz*100;
2123             } else {
2124 
2125                 phase[i] = (uint64_t)embedded_pix_clk_100hz*
2126                     hw_crtc_timing[i].h_total*
2127                     hw_crtc_timing[i].v_total;
2128                 phase[i] = div_u64(phase[i], get_clock_divider(grouped_pipes[i], true));
2129                 modulo[i] = (uint64_t)dp_ref_clk_100hz*
2130                     embedded_h_total*
2131                     embedded_v_total;
2132 
2133                 if (reduceSizeAndFraction(&phase[i],
2134                         &modulo[i], true) == false) {
2135                     /*
2136                      * this will help to stop reporting
2137                      * this timing synchronizable
2138                      */
2139                     DC_SYNC_INFO("Failed to reduce DTO parameters\n");
2140                     grouped_pipes[i]->stream->has_non_synchronizable_pclk = true;
2141                 }
2142             }
2143         }
2144 
2145         for (i = 0; i < group_size; i++) {
2146             if (i != embedded && !grouped_pipes[i]->stream->has_non_synchronizable_pclk) {
2147                 dc->res_pool->dp_clock_source->funcs->override_dp_pix_clk(
2148                     dc->res_pool->dp_clock_source,
2149                     grouped_pipes[i]->stream_res.tg->inst,
2150                     phase[i], modulo[i]);
2151                 dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
2152                     dc->res_pool->dp_clock_source,
2153                     grouped_pipes[i]->stream_res.tg->inst, &pclk);
2154                     grouped_pipes[i]->stream->timing.pix_clk_100hz =
2155                         pclk*get_clock_divider(grouped_pipes[i], false);
2156                 if (master == -1)
2157                     master = i;
2158             }
2159         }
2160 
2161     }
2162 
2163     kfree(hw_crtc_timing);
2164     return master;
2165 }
2166 
2167 void dcn10_enable_vblanks_synchronization(
2168     struct dc *dc,
2169     int group_index,
2170     int group_size,
2171     struct pipe_ctx *grouped_pipes[])
2172 {
2173     struct dc_context *dc_ctx = dc->ctx;
2174     struct output_pixel_processor *opp;
2175     struct timing_generator *tg;
2176     int i, width, height, master;
2177 
2178     for (i = 1; i < group_size; i++) {
2179         opp = grouped_pipes[i]->stream_res.opp;
2180         tg = grouped_pipes[i]->stream_res.tg;
2181         tg->funcs->get_otg_active_size(tg, &width, &height);
2182         if (opp->funcs->opp_program_dpg_dimensions)
2183             opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1);
2184     }
2185 
2186     for (i = 0; i < group_size; i++) {
2187         if (grouped_pipes[i]->stream == NULL)
2188             continue;
2189         grouped_pipes[i]->stream->vblank_synchronized = false;
2190         grouped_pipes[i]->stream->has_non_synchronizable_pclk = false;
2191     }
2192 
2193     DC_SYNC_INFO("Aligning DP DTOs\n");
2194 
2195     master = dcn10_align_pixel_clocks(dc, group_size, grouped_pipes);
2196 
2197     DC_SYNC_INFO("Synchronizing VBlanks\n");
2198 
2199     if (master >= 0) {
2200         for (i = 0; i < group_size; i++) {
2201             if (i != master && !grouped_pipes[i]->stream->has_non_synchronizable_pclk)
2202             grouped_pipes[i]->stream_res.tg->funcs->align_vblanks(
2203                 grouped_pipes[master]->stream_res.tg,
2204                 grouped_pipes[i]->stream_res.tg,
2205                 grouped_pipes[master]->stream->timing.pix_clk_100hz,
2206                 grouped_pipes[i]->stream->timing.pix_clk_100hz,
2207                 get_clock_divider(grouped_pipes[master], false),
2208                 get_clock_divider(grouped_pipes[i], false));
2209                 grouped_pipes[i]->stream->vblank_synchronized = true;
2210         }
2211         grouped_pipes[master]->stream->vblank_synchronized = true;
2212         DC_SYNC_INFO("Sync complete\n");
2213     }
2214 
2215     for (i = 1; i < group_size; i++) {
2216         opp = grouped_pipes[i]->stream_res.opp;
2217         tg = grouped_pipes[i]->stream_res.tg;
2218         tg->funcs->get_otg_active_size(tg, &width, &height);
2219         if (opp->funcs->opp_program_dpg_dimensions)
2220             opp->funcs->opp_program_dpg_dimensions(opp, width, height);
2221     }
2222 }
2223 
2224 void dcn10_enable_timing_synchronization(
2225     struct dc *dc,
2226     int group_index,
2227     int group_size,
2228     struct pipe_ctx *grouped_pipes[])
2229 {
2230     struct dc_context *dc_ctx = dc->ctx;
2231     struct output_pixel_processor *opp;
2232     struct timing_generator *tg;
2233     int i, width, height;
2234 
2235     DC_SYNC_INFO("Setting up OTG reset trigger\n");
2236 
2237     for (i = 1; i < group_size; i++) {
2238         opp = grouped_pipes[i]->stream_res.opp;
2239         tg = grouped_pipes[i]->stream_res.tg;
2240         tg->funcs->get_otg_active_size(tg, &width, &height);
2241         if (opp->funcs->opp_program_dpg_dimensions)
2242             opp->funcs->opp_program_dpg_dimensions(opp, width, 2*(height) + 1);
2243     }
2244 
2245     for (i = 0; i < group_size; i++) {
2246         if (grouped_pipes[i]->stream == NULL)
2247             continue;
2248         grouped_pipes[i]->stream->vblank_synchronized = false;
2249     }
2250 
2251     for (i = 1; i < group_size; i++)
2252         grouped_pipes[i]->stream_res.tg->funcs->enable_reset_trigger(
2253                 grouped_pipes[i]->stream_res.tg,
2254                 grouped_pipes[0]->stream_res.tg->inst);
2255 
2256     DC_SYNC_INFO("Waiting for trigger\n");
2257 
2258     /* Need to get only check 1 pipe for having reset as all the others are
2259      * synchronized. Look at last pipe programmed to reset.
2260      */
2261 
2262     wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[1]->stream_res.tg);
2263     for (i = 1; i < group_size; i++)
2264         grouped_pipes[i]->stream_res.tg->funcs->disable_reset_trigger(
2265                 grouped_pipes[i]->stream_res.tg);
2266 
2267     for (i = 1; i < group_size; i++) {
2268         opp = grouped_pipes[i]->stream_res.opp;
2269         tg = grouped_pipes[i]->stream_res.tg;
2270         tg->funcs->get_otg_active_size(tg, &width, &height);
2271         if (opp->funcs->opp_program_dpg_dimensions)
2272             opp->funcs->opp_program_dpg_dimensions(opp, width, height);
2273     }
2274 
2275     DC_SYNC_INFO("Sync complete\n");
2276 }
2277 
2278 void dcn10_enable_per_frame_crtc_position_reset(
2279     struct dc *dc,
2280     int group_size,
2281     struct pipe_ctx *grouped_pipes[])
2282 {
2283     struct dc_context *dc_ctx = dc->ctx;
2284     int i;
2285 
2286     DC_SYNC_INFO("Setting up\n");
2287     for (i = 0; i < group_size; i++)
2288         if (grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset)
2289             grouped_pipes[i]->stream_res.tg->funcs->enable_crtc_reset(
2290                     grouped_pipes[i]->stream_res.tg,
2291                     0,
2292                     &grouped_pipes[i]->stream->triggered_crtc_reset);
2293 
2294     DC_SYNC_INFO("Waiting for trigger\n");
2295 
2296     for (i = 0; i < group_size; i++)
2297         wait_for_reset_trigger_to_occur(dc_ctx, grouped_pipes[i]->stream_res.tg);
2298 
2299     DC_SYNC_INFO("Multi-display sync is complete\n");
2300 }
2301 
2302 static void mmhub_read_vm_system_aperture_settings(struct dcn10_hubp *hubp1,
2303         struct vm_system_aperture_param *apt,
2304         struct dce_hwseq *hws)
2305 {
2306     PHYSICAL_ADDRESS_LOC physical_page_number;
2307     uint32_t logical_addr_low;
2308     uint32_t logical_addr_high;
2309 
2310     REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_MSB,
2311             PHYSICAL_PAGE_NUMBER_MSB, &physical_page_number.high_part);
2312     REG_GET(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR_LSB,
2313             PHYSICAL_PAGE_NUMBER_LSB, &physical_page_number.low_part);
2314 
2315     REG_GET(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
2316             LOGICAL_ADDR, &logical_addr_low);
2317 
2318     REG_GET(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
2319             LOGICAL_ADDR, &logical_addr_high);
2320 
2321     apt->sys_default.quad_part =  physical_page_number.quad_part << 12;
2322     apt->sys_low.quad_part =  (int64_t)logical_addr_low << 18;
2323     apt->sys_high.quad_part =  (int64_t)logical_addr_high << 18;
2324 }
2325 
2326 /* Temporary read settings, future will get values from kmd directly */
2327 static void mmhub_read_vm_context0_settings(struct dcn10_hubp *hubp1,
2328         struct vm_context0_param *vm0,
2329         struct dce_hwseq *hws)
2330 {
2331     PHYSICAL_ADDRESS_LOC fb_base;
2332     PHYSICAL_ADDRESS_LOC fb_offset;
2333     uint32_t fb_base_value;
2334     uint32_t fb_offset_value;
2335 
2336     REG_GET(DCHUBBUB_SDPIF_FB_BASE, SDPIF_FB_BASE, &fb_base_value);
2337     REG_GET(DCHUBBUB_SDPIF_FB_OFFSET, SDPIF_FB_OFFSET, &fb_offset_value);
2338 
2339     REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32,
2340             PAGE_DIRECTORY_ENTRY_HI32, &vm0->pte_base.high_part);
2341     REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32,
2342             PAGE_DIRECTORY_ENTRY_LO32, &vm0->pte_base.low_part);
2343 
2344     REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_HI32,
2345             LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_start.high_part);
2346     REG_GET(VM_CONTEXT0_PAGE_TABLE_START_ADDR_LO32,
2347             LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_start.low_part);
2348 
2349     REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_HI32,
2350             LOGICAL_PAGE_NUMBER_HI4, &vm0->pte_end.high_part);
2351     REG_GET(VM_CONTEXT0_PAGE_TABLE_END_ADDR_LO32,
2352             LOGICAL_PAGE_NUMBER_LO32, &vm0->pte_end.low_part);
2353 
2354     REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_HI32,
2355             PHYSICAL_PAGE_ADDR_HI4, &vm0->fault_default.high_part);
2356     REG_GET(VM_L2_PROTECTION_FAULT_DEFAULT_ADDR_LO32,
2357             PHYSICAL_PAGE_ADDR_LO32, &vm0->fault_default.low_part);
2358 
2359     /*
2360      * The values in VM_CONTEXT0_PAGE_TABLE_BASE_ADDR is in UMA space.
2361      * Therefore we need to do
2362      * DCN_VM_CONTEXT0_PAGE_TABLE_BASE_ADDR = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR
2363      * - DCHUBBUB_SDPIF_FB_OFFSET + DCHUBBUB_SDPIF_FB_BASE
2364      */
2365     fb_base.quad_part = (uint64_t)fb_base_value << 24;
2366     fb_offset.quad_part = (uint64_t)fb_offset_value << 24;
2367     vm0->pte_base.quad_part += fb_base.quad_part;
2368     vm0->pte_base.quad_part -= fb_offset.quad_part;
2369 }
2370 
2371 
2372 static void dcn10_program_pte_vm(struct dce_hwseq *hws, struct hubp *hubp)
2373 {
2374     struct dcn10_hubp *hubp1 = TO_DCN10_HUBP(hubp);
2375     struct vm_system_aperture_param apt = {0};
2376     struct vm_context0_param vm0 = {0};
2377 
2378     mmhub_read_vm_system_aperture_settings(hubp1, &apt, hws);
2379     mmhub_read_vm_context0_settings(hubp1, &vm0, hws);
2380 
2381     hubp->funcs->hubp_set_vm_system_aperture_settings(hubp, &apt);
2382     hubp->funcs->hubp_set_vm_context0_settings(hubp, &vm0);
2383 }
2384 
2385 static void dcn10_enable_plane(
2386     struct dc *dc,
2387     struct pipe_ctx *pipe_ctx,
2388     struct dc_state *context)
2389 {
2390     struct dce_hwseq *hws = dc->hwseq;
2391 
2392     if (dc->debug.sanity_checks) {
2393         hws->funcs.verify_allow_pstate_change_high(dc);
2394     }
2395 
2396     undo_DEGVIDCN10_253_wa(dc);
2397 
2398     power_on_plane(dc->hwseq,
2399         pipe_ctx->plane_res.hubp->inst);
2400 
2401     /* enable DCFCLK current DCHUB */
2402     pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl(pipe_ctx->plane_res.hubp, true);
2403 
2404     /* make sure OPP_PIPE_CLOCK_EN = 1 */
2405     pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control(
2406             pipe_ctx->stream_res.opp,
2407             true);
2408 
2409     if (dc->config.gpu_vm_support)
2410         dcn10_program_pte_vm(hws, pipe_ctx->plane_res.hubp);
2411 
2412     if (dc->debug.sanity_checks) {
2413         hws->funcs.verify_allow_pstate_change_high(dc);
2414     }
2415 
2416     if (!pipe_ctx->top_pipe
2417         && pipe_ctx->plane_state
2418         && pipe_ctx->plane_state->flip_int_enabled
2419         && pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int)
2420             pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int(pipe_ctx->plane_res.hubp);
2421 
2422 }
2423 
2424 void dcn10_program_gamut_remap(struct pipe_ctx *pipe_ctx)
2425 {
2426     int i = 0;
2427     struct dpp_grph_csc_adjustment adjust;
2428     memset(&adjust, 0, sizeof(adjust));
2429     adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
2430 
2431 
2432     if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
2433         adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
2434         for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
2435             adjust.temperature_matrix[i] =
2436                 pipe_ctx->stream->gamut_remap_matrix.matrix[i];
2437     } else if (pipe_ctx->plane_state &&
2438            pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) {
2439         adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
2440         for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
2441             adjust.temperature_matrix[i] =
2442                 pipe_ctx->plane_state->gamut_remap_matrix.matrix[i];
2443     }
2444 
2445     pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, &adjust);
2446 }
2447 
2448 
2449 static bool dcn10_is_rear_mpo_fix_required(struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace)
2450 {
2451     if (pipe_ctx->plane_state && pipe_ctx->plane_state->layer_index > 0 && is_rgb_cspace(colorspace)) {
2452         if (pipe_ctx->top_pipe) {
2453             struct pipe_ctx *top = pipe_ctx->top_pipe;
2454 
2455             while (top->top_pipe)
2456                 top = top->top_pipe; // Traverse to top pipe_ctx
2457             if (top->plane_state && top->plane_state->layer_index == 0)
2458                 return true; // Front MPO plane not hidden
2459         }
2460     }
2461     return false;
2462 }
2463 
2464 static void dcn10_set_csc_adjustment_rgb_mpo_fix(struct pipe_ctx *pipe_ctx, uint16_t *matrix)
2465 {
2466     // Override rear plane RGB bias to fix MPO brightness
2467     uint16_t rgb_bias = matrix[3];
2468 
2469     matrix[3] = 0;
2470     matrix[7] = 0;
2471     matrix[11] = 0;
2472     pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix);
2473     matrix[3] = rgb_bias;
2474     matrix[7] = rgb_bias;
2475     matrix[11] = rgb_bias;
2476 }
2477 
2478 void dcn10_program_output_csc(struct dc *dc,
2479         struct pipe_ctx *pipe_ctx,
2480         enum dc_color_space colorspace,
2481         uint16_t *matrix,
2482         int opp_id)
2483 {
2484     if (pipe_ctx->stream->csc_color_matrix.enable_adjustment == true) {
2485         if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment != NULL) {
2486 
2487             /* MPO is broken with RGB colorspaces when OCSC matrix
2488              * brightness offset >= 0 on DCN1 due to OCSC before MPC
2489              * Blending adds offsets from front + rear to rear plane
2490              *
2491              * Fix is to set RGB bias to 0 on rear plane, top plane
2492              * black value pixels add offset instead of rear + front
2493              */
2494 
2495             int16_t rgb_bias = matrix[3];
2496             // matrix[3/7/11] are all the same offset value
2497 
2498             if (rgb_bias > 0 && dcn10_is_rear_mpo_fix_required(pipe_ctx, colorspace)) {
2499                 dcn10_set_csc_adjustment_rgb_mpo_fix(pipe_ctx, matrix);
2500             } else {
2501                 pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_adjustment(pipe_ctx->plane_res.dpp, matrix);
2502             }
2503         }
2504     } else {
2505         if (pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default != NULL)
2506             pipe_ctx->plane_res.dpp->funcs->dpp_set_csc_default(pipe_ctx->plane_res.dpp, colorspace);
2507     }
2508 }
2509 
2510 static void dcn10_update_dpp(struct dpp *dpp, struct dc_plane_state *plane_state)
2511 {
2512     struct dc_bias_and_scale bns_params = {0};
2513 
2514     // program the input csc
2515     dpp->funcs->dpp_setup(dpp,
2516             plane_state->format,
2517             EXPANSION_MODE_ZERO,
2518             plane_state->input_csc_color_matrix,
2519             plane_state->color_space,
2520             NULL);
2521 
2522     //set scale and bias registers
2523     build_prescale_params(&bns_params, plane_state);
2524     if (dpp->funcs->dpp_program_bias_and_scale)
2525         dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params);
2526 }
2527 
2528 void dcn10_update_visual_confirm_color(struct dc *dc, struct pipe_ctx *pipe_ctx, struct tg_color *color, int mpcc_id)
2529 {
2530     struct mpc *mpc = dc->res_pool->mpc;
2531 
2532     if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR)
2533         get_hdr_visual_confirm_color(pipe_ctx, color);
2534     else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE)
2535         get_surface_visual_confirm_color(pipe_ctx, color);
2536     else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SWIZZLE)
2537         get_surface_tile_visual_confirm_color(pipe_ctx, color);
2538     else
2539         color_space_to_black_color(
2540                 dc, pipe_ctx->stream->output_color_space, color);
2541 
2542     if (mpc->funcs->set_bg_color)
2543         mpc->funcs->set_bg_color(mpc, color, mpcc_id);
2544 }
2545 
2546 void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
2547 {
2548     struct hubp *hubp = pipe_ctx->plane_res.hubp;
2549     struct mpcc_blnd_cfg blnd_cfg = {0};
2550     bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe;
2551     int mpcc_id;
2552     struct mpcc *new_mpcc;
2553     struct mpc *mpc = dc->res_pool->mpc;
2554     struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params);
2555 
2556     blnd_cfg.overlap_only = false;
2557     blnd_cfg.global_gain = 0xff;
2558 
2559     if (per_pixel_alpha) {
2560         /* DCN1.0 has output CM before MPC which seems to screw with
2561          * pre-multiplied alpha.
2562          */
2563         blnd_cfg.pre_multiplied_alpha = (is_rgb_cspace(
2564                 pipe_ctx->stream->output_color_space)
2565                         && pipe_ctx->plane_state->pre_multiplied_alpha);
2566         if (pipe_ctx->plane_state->global_alpha) {
2567             blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN;
2568             blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value;
2569         } else {
2570             blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA;
2571         }
2572     } else {
2573         blnd_cfg.pre_multiplied_alpha = false;
2574         blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA;
2575     }
2576 
2577     if (pipe_ctx->plane_state->global_alpha)
2578         blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value;
2579     else
2580         blnd_cfg.global_alpha = 0xff;
2581 
2582     /*
2583      * TODO: remove hack
2584      * Note: currently there is a bug in init_hw such that
2585      * on resume from hibernate, BIOS sets up MPCC0, and
2586      * we do mpcc_remove but the mpcc cannot go to idle
2587      * after remove. This cause us to pick mpcc1 here,
2588      * which causes a pstate hang for yet unknown reason.
2589      */
2590     mpcc_id = hubp->inst;
2591 
2592     /* If there is no full update, don't need to touch MPC tree*/
2593     if (!pipe_ctx->plane_state->update_flags.bits.full_update) {
2594         mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
2595         dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id);
2596         return;
2597     }
2598 
2599     /* check if this MPCC is already being used */
2600     new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id);
2601     /* remove MPCC if being used */
2602     if (new_mpcc != NULL)
2603         mpc->funcs->remove_mpcc(mpc, mpc_tree_params, new_mpcc);
2604     else
2605         if (dc->debug.sanity_checks)
2606             mpc->funcs->assert_mpcc_idle_before_connect(
2607                     dc->res_pool->mpc, mpcc_id);
2608 
2609     /* Call MPC to insert new plane */
2610     new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc,
2611             mpc_tree_params,
2612             &blnd_cfg,
2613             NULL,
2614             NULL,
2615             hubp->inst,
2616             mpcc_id);
2617     dc->hwss.update_visual_confirm_color(dc, pipe_ctx, &blnd_cfg.black_color, mpcc_id);
2618 
2619     ASSERT(new_mpcc != NULL);
2620     hubp->opp_id = pipe_ctx->stream_res.opp->inst;
2621     hubp->mpcc_id = mpcc_id;
2622 }
2623 
2624 static void update_scaler(struct pipe_ctx *pipe_ctx)
2625 {
2626     bool per_pixel_alpha =
2627             pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe;
2628 
2629     pipe_ctx->plane_res.scl_data.lb_params.alpha_en = per_pixel_alpha;
2630     pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_36BPP;
2631     /* scaler configuration */
2632     pipe_ctx->plane_res.dpp->funcs->dpp_set_scaler(
2633             pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data);
2634 }
2635 
2636 static void dcn10_update_dchubp_dpp(
2637     struct dc *dc,
2638     struct pipe_ctx *pipe_ctx,
2639     struct dc_state *context)
2640 {
2641     struct dce_hwseq *hws = dc->hwseq;
2642     struct hubp *hubp = pipe_ctx->plane_res.hubp;
2643     struct dpp *dpp = pipe_ctx->plane_res.dpp;
2644     struct dc_plane_state *plane_state = pipe_ctx->plane_state;
2645     struct plane_size size = plane_state->plane_size;
2646     unsigned int compat_level = 0;
2647     bool should_divided_by_2 = false;
2648 
2649     /* depends on DML calculation, DPP clock value may change dynamically */
2650     /* If request max dpp clk is lower than current dispclk, no need to
2651      * divided by 2
2652      */
2653     if (plane_state->update_flags.bits.full_update) {
2654 
2655         /* new calculated dispclk, dppclk are stored in
2656          * context->bw_ctx.bw.dcn.clk.dispclk_khz / dppclk_khz. current
2657          * dispclk, dppclk are from dc->clk_mgr->clks.dispclk_khz.
2658          * dcn10_validate_bandwidth compute new dispclk, dppclk.
2659          * dispclk will put in use after optimize_bandwidth when
2660          * ramp_up_dispclk_with_dpp is called.
2661          * there are two places for dppclk be put in use. One location
2662          * is the same as the location as dispclk. Another is within
2663          * update_dchubp_dpp which happens between pre_bandwidth and
2664          * optimize_bandwidth.
2665          * dppclk updated within update_dchubp_dpp will cause new
2666          * clock values of dispclk and dppclk not be in use at the same
2667          * time. when clocks are decreased, this may cause dppclk is
2668          * lower than previous configuration and let pipe stuck.
2669          * for example, eDP + external dp,  change resolution of DP from
2670          * 1920x1080x144hz to 1280x960x60hz.
2671          * before change: dispclk = 337889 dppclk = 337889
2672          * change mode, dcn10_validate_bandwidth calculate
2673          *                dispclk = 143122 dppclk = 143122
2674          * update_dchubp_dpp be executed before dispclk be updated,
2675          * dispclk = 337889, but dppclk use new value dispclk /2 =
2676          * 168944. this will cause pipe pstate warning issue.
2677          * solution: between pre_bandwidth and optimize_bandwidth, while
2678          * dispclk is going to be decreased, keep dppclk = dispclk
2679          **/
2680         if (context->bw_ctx.bw.dcn.clk.dispclk_khz <
2681                 dc->clk_mgr->clks.dispclk_khz)
2682             should_divided_by_2 = false;
2683         else
2684             should_divided_by_2 =
2685                     context->bw_ctx.bw.dcn.clk.dppclk_khz <=
2686                     dc->clk_mgr->clks.dispclk_khz / 2;
2687 
2688         dpp->funcs->dpp_dppclk_control(
2689                 dpp,
2690                 should_divided_by_2,
2691                 true);
2692 
2693         if (dc->res_pool->dccg)
2694             dc->res_pool->dccg->funcs->update_dpp_dto(
2695                     dc->res_pool->dccg,
2696                     dpp->inst,
2697                     pipe_ctx->plane_res.bw.dppclk_khz);
2698         else
2699             dc->clk_mgr->clks.dppclk_khz = should_divided_by_2 ?
2700                         dc->clk_mgr->clks.dispclk_khz / 2 :
2701                             dc->clk_mgr->clks.dispclk_khz;
2702     }
2703 
2704     /* TODO: Need input parameter to tell current DCHUB pipe tie to which OTG
2705      * VTG is within DCHUBBUB which is commond block share by each pipe HUBP.
2706      * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG
2707      */
2708     if (plane_state->update_flags.bits.full_update) {
2709         hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst);
2710 
2711         hubp->funcs->hubp_setup(
2712             hubp,
2713             &pipe_ctx->dlg_regs,
2714             &pipe_ctx->ttu_regs,
2715             &pipe_ctx->rq_regs,
2716             &pipe_ctx->pipe_dlg_param);
2717         hubp->funcs->hubp_setup_interdependent(
2718             hubp,
2719             &pipe_ctx->dlg_regs,
2720             &pipe_ctx->ttu_regs);
2721     }
2722 
2723     size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
2724 
2725     if (plane_state->update_flags.bits.full_update ||
2726         plane_state->update_flags.bits.bpp_change)
2727         dcn10_update_dpp(dpp, plane_state);
2728 
2729     if (plane_state->update_flags.bits.full_update ||
2730         plane_state->update_flags.bits.per_pixel_alpha_change ||
2731         plane_state->update_flags.bits.global_alpha_change)
2732         hws->funcs.update_mpcc(dc, pipe_ctx);
2733 
2734     if (plane_state->update_flags.bits.full_update ||
2735         plane_state->update_flags.bits.per_pixel_alpha_change ||
2736         plane_state->update_flags.bits.global_alpha_change ||
2737         plane_state->update_flags.bits.scaling_change ||
2738         plane_state->update_flags.bits.position_change) {
2739         update_scaler(pipe_ctx);
2740     }
2741 
2742     if (plane_state->update_flags.bits.full_update ||
2743         plane_state->update_flags.bits.scaling_change ||
2744         plane_state->update_flags.bits.position_change) {
2745         hubp->funcs->mem_program_viewport(
2746             hubp,
2747             &pipe_ctx->plane_res.scl_data.viewport,
2748             &pipe_ctx->plane_res.scl_data.viewport_c);
2749     }
2750 
2751     if (pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
2752         dc->hwss.set_cursor_position(pipe_ctx);
2753         dc->hwss.set_cursor_attribute(pipe_ctx);
2754 
2755         if (dc->hwss.set_cursor_sdr_white_level)
2756             dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
2757     }
2758 
2759     if (plane_state->update_flags.bits.full_update) {
2760         /*gamut remap*/
2761         dc->hwss.program_gamut_remap(pipe_ctx);
2762 
2763         dc->hwss.program_output_csc(dc,
2764                 pipe_ctx,
2765                 pipe_ctx->stream->output_color_space,
2766                 pipe_ctx->stream->csc_color_matrix.matrix,
2767                 pipe_ctx->stream_res.opp->inst);
2768     }
2769 
2770     if (plane_state->update_flags.bits.full_update ||
2771         plane_state->update_flags.bits.pixel_format_change ||
2772         plane_state->update_flags.bits.horizontal_mirror_change ||
2773         plane_state->update_flags.bits.rotation_change ||
2774         plane_state->update_flags.bits.swizzle_change ||
2775         plane_state->update_flags.bits.dcc_change ||
2776         plane_state->update_flags.bits.bpp_change ||
2777         plane_state->update_flags.bits.scaling_change ||
2778         plane_state->update_flags.bits.plane_size_change) {
2779         hubp->funcs->hubp_program_surface_config(
2780             hubp,
2781             plane_state->format,
2782             &plane_state->tiling_info,
2783             &size,
2784             plane_state->rotation,
2785             &plane_state->dcc,
2786             plane_state->horizontal_mirror,
2787             compat_level);
2788     }
2789 
2790     hubp->power_gated = false;
2791 
2792     hws->funcs.update_plane_addr(dc, pipe_ctx);
2793 
2794     if (is_pipe_tree_visible(pipe_ctx))
2795         hubp->funcs->set_blank(hubp, false);
2796 }
2797 
2798 void dcn10_blank_pixel_data(
2799         struct dc *dc,
2800         struct pipe_ctx *pipe_ctx,
2801         bool blank)
2802 {
2803     enum dc_color_space color_space;
2804     struct tg_color black_color = {0};
2805     struct stream_resource *stream_res = &pipe_ctx->stream_res;
2806     struct dc_stream_state *stream = pipe_ctx->stream;
2807 
2808     /* program otg blank color */
2809     color_space = stream->output_color_space;
2810     color_space_to_black_color(dc, color_space, &black_color);
2811 
2812     /*
2813      * The way 420 is packed, 2 channels carry Y component, 1 channel
2814      * alternate between Cb and Cr, so both channels need the pixel
2815      * value for Y
2816      */
2817     if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)
2818         black_color.color_r_cr = black_color.color_g_y;
2819 
2820 
2821     if (stream_res->tg->funcs->set_blank_color)
2822         stream_res->tg->funcs->set_blank_color(
2823                 stream_res->tg,
2824                 &black_color);
2825 
2826     if (!blank) {
2827         if (stream_res->tg->funcs->set_blank)
2828             stream_res->tg->funcs->set_blank(stream_res->tg, blank);
2829         if (stream_res->abm) {
2830             dc->hwss.set_pipe(pipe_ctx);
2831             stream_res->abm->funcs->set_abm_level(stream_res->abm, stream->abm_level);
2832         }
2833     } else if (blank) {
2834         dc->hwss.set_abm_immediate_disable(pipe_ctx);
2835         if (stream_res->tg->funcs->set_blank) {
2836             stream_res->tg->funcs->wait_for_state(stream_res->tg, CRTC_STATE_VBLANK);
2837             stream_res->tg->funcs->set_blank(stream_res->tg, blank);
2838         }
2839     }
2840 }
2841 
2842 void dcn10_set_hdr_multiplier(struct pipe_ctx *pipe_ctx)
2843 {
2844     struct fixed31_32 multiplier = pipe_ctx->plane_state->hdr_mult;
2845     uint32_t hw_mult = 0x1f000; // 1.0 default multiplier
2846     struct custom_float_format fmt;
2847 
2848     fmt.exponenta_bits = 6;
2849     fmt.mantissa_bits = 12;
2850     fmt.sign = true;
2851 
2852 
2853     if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0))) // check != 0
2854         convert_to_custom_float_format(multiplier, &fmt, &hw_mult);
2855 
2856     pipe_ctx->plane_res.dpp->funcs->dpp_set_hdr_multiplier(
2857             pipe_ctx->plane_res.dpp, hw_mult);
2858 }
2859 
2860 void dcn10_program_pipe(
2861         struct dc *dc,
2862         struct pipe_ctx *pipe_ctx,
2863         struct dc_state *context)
2864 {
2865     struct dce_hwseq *hws = dc->hwseq;
2866 
2867     if (pipe_ctx->top_pipe == NULL) {
2868         bool blank = !is_pipe_tree_visible(pipe_ctx);
2869 
2870         pipe_ctx->stream_res.tg->funcs->program_global_sync(
2871                 pipe_ctx->stream_res.tg,
2872                 pipe_ctx->pipe_dlg_param.vready_offset,
2873                 pipe_ctx->pipe_dlg_param.vstartup_start,
2874                 pipe_ctx->pipe_dlg_param.vupdate_offset,
2875                 pipe_ctx->pipe_dlg_param.vupdate_width);
2876 
2877         pipe_ctx->stream_res.tg->funcs->set_vtg_params(
2878                 pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true);
2879 
2880         if (hws->funcs.setup_vupdate_interrupt)
2881             hws->funcs.setup_vupdate_interrupt(dc, pipe_ctx);
2882 
2883         hws->funcs.blank_pixel_data(dc, pipe_ctx, blank);
2884     }
2885 
2886     if (pipe_ctx->plane_state->update_flags.bits.full_update)
2887         dcn10_enable_plane(dc, pipe_ctx, context);
2888 
2889     dcn10_update_dchubp_dpp(dc, pipe_ctx, context);
2890 
2891     hws->funcs.set_hdr_multiplier(pipe_ctx);
2892 
2893     if (pipe_ctx->plane_state->update_flags.bits.full_update ||
2894             pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
2895             pipe_ctx->plane_state->update_flags.bits.gamma_change)
2896         hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
2897 
2898     /* dcn10_translate_regamma_to_hw_format takes 750us to finish
2899      * only do gamma programming for full update.
2900      * TODO: This can be further optimized/cleaned up
2901      * Always call this for now since it does memcmp inside before
2902      * doing heavy calculation and programming
2903      */
2904     if (pipe_ctx->plane_state->update_flags.bits.full_update)
2905         hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
2906 }
2907 
2908 void dcn10_wait_for_pending_cleared(struct dc *dc,
2909         struct dc_state *context)
2910 {
2911         struct pipe_ctx *pipe_ctx;
2912         struct timing_generator *tg;
2913         int i;
2914 
2915         for (i = 0; i < dc->res_pool->pipe_count; i++) {
2916             pipe_ctx = &context->res_ctx.pipe_ctx[i];
2917             tg = pipe_ctx->stream_res.tg;
2918 
2919             /*
2920              * Only wait for top pipe's tg penindg bit
2921              * Also skip if pipe is disabled.
2922              */
2923             if (pipe_ctx->top_pipe ||
2924                 !pipe_ctx->stream || !pipe_ctx->plane_state ||
2925                 !tg->funcs->is_tg_enabled(tg))
2926                 continue;
2927 
2928             /*
2929              * Wait for VBLANK then VACTIVE to ensure we get VUPDATE.
2930              * For some reason waiting for OTG_UPDATE_PENDING cleared
2931              * seems to not trigger the update right away, and if we
2932              * lock again before VUPDATE then we don't get a separated
2933              * operation.
2934              */
2935             pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
2936             pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
2937         }
2938 }
2939 
2940 void dcn10_post_unlock_program_front_end(
2941         struct dc *dc,
2942         struct dc_state *context)
2943 {
2944     int i;
2945 
2946     DC_LOGGER_INIT(dc->ctx->logger);
2947 
2948     for (i = 0; i < dc->res_pool->pipe_count; i++) {
2949         struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
2950 
2951         if (!pipe_ctx->top_pipe &&
2952             !pipe_ctx->prev_odm_pipe &&
2953             pipe_ctx->stream) {
2954             struct timing_generator *tg = pipe_ctx->stream_res.tg;
2955 
2956             if (context->stream_status[i].plane_count == 0)
2957                 false_optc_underflow_wa(dc, pipe_ctx->stream, tg);
2958         }
2959     }
2960 
2961     for (i = 0; i < dc->res_pool->pipe_count; i++)
2962         if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable)
2963             dc->hwss.disable_plane(dc, &dc->current_state->res_ctx.pipe_ctx[i]);
2964 
2965     for (i = 0; i < dc->res_pool->pipe_count; i++)
2966         if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable) {
2967             dc->hwss.optimize_bandwidth(dc, context);
2968             break;
2969         }
2970 
2971     if (dc->hwseq->wa.DEGVIDCN10_254)
2972         hubbub1_wm_change_req_wa(dc->res_pool->hubbub);
2973 }
2974 
2975 static void dcn10_stereo_hw_frame_pack_wa(struct dc *dc, struct dc_state *context)
2976 {
2977     uint8_t i;
2978 
2979     for (i = 0; i < context->stream_count; i++) {
2980         if (context->streams[i]->timing.timing_3d_format
2981                 == TIMING_3D_FORMAT_HW_FRAME_PACKING) {
2982             /*
2983              * Disable stutter
2984              */
2985             hubbub1_allow_self_refresh_control(dc->res_pool->hubbub, false);
2986             break;
2987         }
2988     }
2989 }
2990 
2991 void dcn10_prepare_bandwidth(
2992         struct dc *dc,
2993         struct dc_state *context)
2994 {
2995     struct dce_hwseq *hws = dc->hwseq;
2996     struct hubbub *hubbub = dc->res_pool->hubbub;
2997 
2998     if (dc->debug.sanity_checks)
2999         hws->funcs.verify_allow_pstate_change_high(dc);
3000 
3001     if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
3002         if (context->stream_count == 0)
3003             context->bw_ctx.bw.dcn.clk.phyclk_khz = 0;
3004 
3005         dc->clk_mgr->funcs->update_clocks(
3006                 dc->clk_mgr,
3007                 context,
3008                 false);
3009     }
3010 
3011     dc->wm_optimized_required = hubbub->funcs->program_watermarks(hubbub,
3012             &context->bw_ctx.bw.dcn.watermarks,
3013             dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
3014             true);
3015     dcn10_stereo_hw_frame_pack_wa(dc, context);
3016 
3017     if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) {
3018         DC_FP_START();
3019         dcn_bw_notify_pplib_of_wm_ranges(dc);
3020         DC_FP_END();
3021     }
3022 
3023     if (dc->debug.sanity_checks)
3024         hws->funcs.verify_allow_pstate_change_high(dc);
3025 }
3026 
3027 void dcn10_optimize_bandwidth(
3028         struct dc *dc,
3029         struct dc_state *context)
3030 {
3031     struct dce_hwseq *hws = dc->hwseq;
3032     struct hubbub *hubbub = dc->res_pool->hubbub;
3033 
3034     if (dc->debug.sanity_checks)
3035         hws->funcs.verify_allow_pstate_change_high(dc);
3036 
3037     if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) {
3038         if (context->stream_count == 0)
3039             context->bw_ctx.bw.dcn.clk.phyclk_khz = 0;
3040 
3041         dc->clk_mgr->funcs->update_clocks(
3042                 dc->clk_mgr,
3043                 context,
3044                 true);
3045     }
3046 
3047     hubbub->funcs->program_watermarks(hubbub,
3048             &context->bw_ctx.bw.dcn.watermarks,
3049             dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
3050             true);
3051 
3052     dcn10_stereo_hw_frame_pack_wa(dc, context);
3053 
3054     if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) {
3055         DC_FP_START();
3056         dcn_bw_notify_pplib_of_wm_ranges(dc);
3057         DC_FP_END();
3058     }
3059 
3060     if (dc->debug.sanity_checks)
3061         hws->funcs.verify_allow_pstate_change_high(dc);
3062 }
3063 
3064 void dcn10_set_drr(struct pipe_ctx **pipe_ctx,
3065         int num_pipes, struct dc_crtc_timing_adjust adjust)
3066 {
3067     int i = 0;
3068     struct drr_params params = {0};
3069     // DRR set trigger event mapped to OTG_TRIG_A (bit 11) for manual control flow
3070     unsigned int event_triggers = 0x800;
3071     // Note DRR trigger events are generated regardless of whether num frames met.
3072     unsigned int num_frames = 2;
3073 
3074     params.vertical_total_max = adjust.v_total_max;
3075     params.vertical_total_min = adjust.v_total_min;
3076     params.vertical_total_mid = adjust.v_total_mid;
3077     params.vertical_total_mid_frame_num = adjust.v_total_mid_frame_num;
3078     /* TODO: If multiple pipes are to be supported, you need
3079      * some GSL stuff. Static screen triggers may be programmed differently
3080      * as well.
3081      */
3082     for (i = 0; i < num_pipes; i++) {
3083         if ((pipe_ctx[i]->stream_res.tg != NULL) && pipe_ctx[i]->stream_res.tg->funcs) {
3084             if (pipe_ctx[i]->stream_res.tg->funcs->set_drr)
3085                 pipe_ctx[i]->stream_res.tg->funcs->set_drr(
3086                     pipe_ctx[i]->stream_res.tg, &params);
3087             if (adjust.v_total_max != 0 && adjust.v_total_min != 0)
3088                 if (pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control)
3089                     pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control(
3090                         pipe_ctx[i]->stream_res.tg,
3091                         event_triggers, num_frames);
3092         }
3093     }
3094 }
3095 
3096 void dcn10_get_position(struct pipe_ctx **pipe_ctx,
3097         int num_pipes,
3098         struct crtc_position *position)
3099 {
3100     int i = 0;
3101 
3102     /* TODO: handle pipes > 1
3103      */
3104     for (i = 0; i < num_pipes; i++)
3105         pipe_ctx[i]->stream_res.tg->funcs->get_position(pipe_ctx[i]->stream_res.tg, position);
3106 }
3107 
3108 void dcn10_set_static_screen_control(struct pipe_ctx **pipe_ctx,
3109         int num_pipes, const struct dc_static_screen_params *params)
3110 {
3111     unsigned int i;
3112     unsigned int triggers = 0;
3113 
3114     if (params->triggers.surface_update)
3115         triggers |= 0x80;
3116     if (params->triggers.cursor_update)
3117         triggers |= 0x2;
3118     if (params->triggers.force_trigger)
3119         triggers |= 0x1;
3120 
3121     for (i = 0; i < num_pipes; i++)
3122         pipe_ctx[i]->stream_res.tg->funcs->
3123             set_static_screen_control(pipe_ctx[i]->stream_res.tg,
3124                     triggers, params->num_frames);
3125 }
3126 
3127 static void dcn10_config_stereo_parameters(
3128         struct dc_stream_state *stream, struct crtc_stereo_flags *flags)
3129 {
3130     enum view_3d_format view_format = stream->view_format;
3131     enum dc_timing_3d_format timing_3d_format =\
3132             stream->timing.timing_3d_format;
3133     bool non_stereo_timing = false;
3134 
3135     if (timing_3d_format == TIMING_3D_FORMAT_NONE ||
3136         timing_3d_format == TIMING_3D_FORMAT_SIDE_BY_SIDE ||
3137         timing_3d_format == TIMING_3D_FORMAT_TOP_AND_BOTTOM)
3138         non_stereo_timing = true;
3139 
3140     if (non_stereo_timing == false &&
3141         view_format == VIEW_3D_FORMAT_FRAME_SEQUENTIAL) {
3142 
3143         flags->PROGRAM_STEREO         = 1;
3144         flags->PROGRAM_POLARITY       = 1;
3145         if (timing_3d_format == TIMING_3D_FORMAT_FRAME_ALTERNATE ||
3146             timing_3d_format == TIMING_3D_FORMAT_INBAND_FA ||
3147             timing_3d_format == TIMING_3D_FORMAT_DP_HDMI_INBAND_FA ||
3148             timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) {
3149             enum display_dongle_type dongle = \
3150                     stream->link->ddc->dongle_type;
3151             if (dongle == DISPLAY_DONGLE_DP_VGA_CONVERTER ||
3152                 dongle == DISPLAY_DONGLE_DP_DVI_CONVERTER ||
3153                 dongle == DISPLAY_DONGLE_DP_HDMI_CONVERTER)
3154                 flags->DISABLE_STEREO_DP_SYNC = 1;
3155         }
3156         flags->RIGHT_EYE_POLARITY =\
3157                 stream->timing.flags.RIGHT_EYE_3D_POLARITY;
3158         if (timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING)
3159             flags->FRAME_PACKED = 1;
3160     }
3161 
3162     return;
3163 }
3164 
3165 void dcn10_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc)
3166 {
3167     struct crtc_stereo_flags flags = { 0 };
3168     struct dc_stream_state *stream = pipe_ctx->stream;
3169 
3170     dcn10_config_stereo_parameters(stream, &flags);
3171 
3172     if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_SIDEBAND_FA) {
3173         if (!dc_set_generic_gpio_for_stereo(true, dc->ctx->gpio_service))
3174             dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service);
3175     } else {
3176         dc_set_generic_gpio_for_stereo(false, dc->ctx->gpio_service);
3177     }
3178 
3179     pipe_ctx->stream_res.opp->funcs->opp_program_stereo(
3180         pipe_ctx->stream_res.opp,
3181         flags.PROGRAM_STEREO == 1,
3182         &stream->timing);
3183 
3184     pipe_ctx->stream_res.tg->funcs->program_stereo(
3185         pipe_ctx->stream_res.tg,
3186         &stream->timing,
3187         &flags);
3188 
3189     return;
3190 }
3191 
3192 static struct hubp *get_hubp_by_inst(struct resource_pool *res_pool, int mpcc_inst)
3193 {
3194     int i;
3195 
3196     for (i = 0; i < res_pool->pipe_count; i++) {
3197         if (res_pool->hubps[i]->inst == mpcc_inst)
3198             return res_pool->hubps[i];
3199     }
3200     ASSERT(false);
3201     return NULL;
3202 }
3203 
3204 void dcn10_wait_for_mpcc_disconnect(
3205         struct dc *dc,
3206         struct resource_pool *res_pool,
3207         struct pipe_ctx *pipe_ctx)
3208 {
3209     struct dce_hwseq *hws = dc->hwseq;
3210     int mpcc_inst;
3211 
3212     if (dc->debug.sanity_checks) {
3213         hws->funcs.verify_allow_pstate_change_high(dc);
3214     }
3215 
3216     if (!pipe_ctx->stream_res.opp)
3217         return;
3218 
3219     for (mpcc_inst = 0; mpcc_inst < MAX_PIPES; mpcc_inst++) {
3220         if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst]) {
3221             struct hubp *hubp = get_hubp_by_inst(res_pool, mpcc_inst);
3222 
3223             if (pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg))
3224                 res_pool->mpc->funcs->wait_for_idle(res_pool->mpc, mpcc_inst);
3225             pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst] = false;
3226             hubp->funcs->set_blank(hubp, true);
3227         }
3228     }
3229 
3230     if (dc->debug.sanity_checks) {
3231         hws->funcs.verify_allow_pstate_change_high(dc);
3232     }
3233 
3234 }
3235 
3236 bool dcn10_dummy_display_power_gating(
3237     struct dc *dc,
3238     uint8_t controller_id,
3239     struct dc_bios *dcb,
3240     enum pipe_gating_control power_gating)
3241 {
3242     return true;
3243 }
3244 
3245 void dcn10_update_pending_status(struct pipe_ctx *pipe_ctx)
3246 {
3247     struct dc_plane_state *plane_state = pipe_ctx->plane_state;
3248     struct timing_generator *tg = pipe_ctx->stream_res.tg;
3249     bool flip_pending;
3250     struct dc *dc = pipe_ctx->stream->ctx->dc;
3251 
3252     if (plane_state == NULL)
3253         return;
3254 
3255     flip_pending = pipe_ctx->plane_res.hubp->funcs->hubp_is_flip_pending(
3256                     pipe_ctx->plane_res.hubp);
3257 
3258     plane_state->status.is_flip_pending = plane_state->status.is_flip_pending || flip_pending;
3259 
3260     if (!flip_pending)
3261         plane_state->status.current_address = plane_state->status.requested_address;
3262 
3263     if (plane_state->status.current_address.type == PLN_ADDR_TYPE_GRPH_STEREO &&
3264             tg->funcs->is_stereo_left_eye) {
3265         plane_state->status.is_right_eye =
3266                 !tg->funcs->is_stereo_left_eye(pipe_ctx->stream_res.tg);
3267     }
3268 
3269     if (dc->hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied) {
3270         struct dce_hwseq *hwseq = dc->hwseq;
3271         struct timing_generator *tg = dc->res_pool->timing_generators[0];
3272         unsigned int cur_frame = tg->funcs->get_frame_count(tg);
3273 
3274         if (cur_frame != hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied_on_frame) {
3275             struct hubbub *hubbub = dc->res_pool->hubbub;
3276 
3277             hubbub->funcs->allow_self_refresh_control(hubbub, !dc->debug.disable_stutter);
3278             hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied = false;
3279         }
3280     }
3281 }
3282 
3283 void dcn10_update_dchub(struct dce_hwseq *hws, struct dchub_init_data *dh_data)
3284 {
3285     struct hubbub *hubbub = hws->ctx->dc->res_pool->hubbub;
3286 
3287     /* In DCN, this programming sequence is owned by the hubbub */
3288     hubbub->funcs->update_dchub(hubbub, dh_data);
3289 }
3290 
3291 static bool dcn10_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)
3292 {
3293     struct pipe_ctx *test_pipe, *split_pipe;
3294     const struct scaler_data *scl_data = &pipe_ctx->plane_res.scl_data;
3295     struct rect r1 = scl_data->recout, r2, r2_half;
3296     int r1_r = r1.x + r1.width, r1_b = r1.y + r1.height, r2_r, r2_b;
3297     int cur_layer = pipe_ctx->plane_state->layer_index;
3298 
3299     /**
3300      * Disable the cursor if there's another pipe above this with a
3301      * plane that contains this pipe's viewport to prevent double cursor
3302      * and incorrect scaling artifacts.
3303      */
3304     for (test_pipe = pipe_ctx->top_pipe; test_pipe;
3305          test_pipe = test_pipe->top_pipe) {
3306         // Skip invisible layer and pipe-split plane on same layer
3307         if (!test_pipe->plane_state->visible || test_pipe->plane_state->layer_index == cur_layer)
3308             continue;
3309 
3310         r2 = test_pipe->plane_res.scl_data.recout;
3311         r2_r = r2.x + r2.width;
3312         r2_b = r2.y + r2.height;
3313         split_pipe = test_pipe;
3314 
3315         /**
3316          * There is another half plane on same layer because of
3317          * pipe-split, merge together per same height.
3318          */
3319         for (split_pipe = pipe_ctx->top_pipe; split_pipe;
3320              split_pipe = split_pipe->top_pipe)
3321             if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) {
3322                 r2_half = split_pipe->plane_res.scl_data.recout;
3323                 r2.x = (r2_half.x < r2.x) ? r2_half.x : r2.x;
3324                 r2.width = r2.width + r2_half.width;
3325                 r2_r = r2.x + r2.width;
3326                 break;
3327             }
3328 
3329         if (r1.x >= r2.x && r1.y >= r2.y && r1_r <= r2_r && r1_b <= r2_b)
3330             return true;
3331     }
3332 
3333     return false;
3334 }
3335 
3336 static bool dcn10_dmub_should_update_cursor_data(
3337         struct pipe_ctx *pipe_ctx,
3338         struct dc_debug_options *debug)
3339 {
3340     if (pipe_ctx->plane_state->address.type == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE)
3341         return false;
3342 
3343     if (pipe_ctx->stream->link->psr_settings.psr_version == DC_PSR_VERSION_SU_1)
3344         return true;
3345 
3346     if (pipe_ctx->stream->link->psr_settings.psr_version == DC_PSR_VERSION_1 &&
3347         debug->enable_sw_cntl_psr)
3348         return true;
3349 
3350     return false;
3351 }
3352 
3353 static void dcn10_dmub_update_cursor_data(
3354         struct pipe_ctx *pipe_ctx,
3355         struct hubp *hubp,
3356         const struct dc_cursor_mi_param *param,
3357         const struct dc_cursor_position *cur_pos,
3358         const struct dc_cursor_attributes *cur_attr)
3359 {
3360     union dmub_rb_cmd cmd;
3361     struct dmub_cmd_update_cursor_info_data *update_cursor_info;
3362     const struct dc_cursor_position *pos;
3363     const struct dc_cursor_attributes *attr;
3364     int src_x_offset = 0;
3365     int src_y_offset = 0;
3366     int x_hotspot = 0;
3367     int cursor_height = 0;
3368     int cursor_width = 0;
3369     uint32_t cur_en = 0;
3370     unsigned int panel_inst = 0;
3371 
3372     struct dc_debug_options *debug = &hubp->ctx->dc->debug;
3373 
3374     if (!dcn10_dmub_should_update_cursor_data(pipe_ctx, debug))
3375         return;
3376     /**
3377      * if cur_pos == NULL means the caller is from cursor_set_attribute
3378      * then driver use previous cursor position data
3379      * if cur_attr == NULL means the caller is from cursor_set_position
3380      * then driver use previous cursor attribute
3381      * if cur_pos or cur_attr is not NULL then update it
3382      */
3383     if (cur_pos != NULL)
3384         pos = cur_pos;
3385     else
3386         pos = &hubp->curs_pos;
3387 
3388     if (cur_attr != NULL)
3389         attr = cur_attr;
3390     else
3391         attr = &hubp->curs_attr;
3392 
3393     if (!dc_get_edp_link_panel_inst(hubp->ctx->dc, pipe_ctx->stream->link, &panel_inst))
3394         return;
3395 
3396     src_x_offset = pos->x - pos->x_hotspot - param->viewport.x;
3397     src_y_offset = pos->y - pos->y_hotspot - param->viewport.y;
3398     x_hotspot = pos->x_hotspot;
3399     cursor_height = (int)attr->height;
3400     cursor_width = (int)attr->width;
3401     cur_en = pos->enable ? 1:0;
3402 
3403     // Rotated cursor width/height and hotspots tweaks for offset calculation
3404     if (param->rotation == ROTATION_ANGLE_90 || param->rotation == ROTATION_ANGLE_270) {
3405         swap(cursor_height, cursor_width);
3406         if (param->rotation == ROTATION_ANGLE_90) {
3407             src_x_offset = pos->x - pos->y_hotspot - param->viewport.x;
3408             src_y_offset = pos->y - pos->x_hotspot - param->viewport.y;
3409         }
3410     } else if (param->rotation == ROTATION_ANGLE_180) {
3411         src_x_offset = pos->x - param->viewport.x;
3412         src_y_offset = pos->y - param->viewport.y;
3413     }
3414 
3415     if (param->mirror) {
3416         x_hotspot = param->viewport.width - x_hotspot;
3417         src_x_offset = param->viewport.x + param->viewport.width - src_x_offset;
3418     }
3419 
3420     if (src_x_offset >= (int)param->viewport.width)
3421         cur_en = 0;  /* not visible beyond right edge*/
3422 
3423     if (src_x_offset + cursor_width <= 0)
3424         cur_en = 0;  /* not visible beyond left edge*/
3425 
3426     if (src_y_offset >= (int)param->viewport.height)
3427         cur_en = 0;  /* not visible beyond bottom edge*/
3428 
3429     if (src_y_offset + cursor_height <= 0)
3430         cur_en = 0;  /* not visible beyond top edge*/
3431 
3432     // Cursor bitmaps have different hotspot values
3433     // There's a possibility that the above logic returns a negative value, so we clamp them to 0
3434     if (src_x_offset < 0)
3435         src_x_offset = 0;
3436     if (src_y_offset < 0)
3437         src_y_offset = 0;
3438 
3439     memset(&cmd, 0x0, sizeof(cmd));
3440     cmd.update_cursor_info.header.type = DMUB_CMD__UPDATE_CURSOR_INFO;
3441     cmd.update_cursor_info.header.payload_bytes =
3442             sizeof(cmd.update_cursor_info.update_cursor_info_data);
3443     update_cursor_info = &cmd.update_cursor_info.update_cursor_info_data;
3444     update_cursor_info->cursor_rect.x = src_x_offset + param->viewport.x;
3445     update_cursor_info->cursor_rect.y = src_y_offset + param->viewport.y;
3446     update_cursor_info->cursor_rect.width = attr->width;
3447     update_cursor_info->cursor_rect.height = attr->height;
3448     update_cursor_info->enable = cur_en;
3449     update_cursor_info->pipe_idx = pipe_ctx->pipe_idx;
3450     update_cursor_info->cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1;
3451     update_cursor_info->panel_inst = panel_inst;
3452     dc_dmub_srv_cmd_queue(pipe_ctx->stream->ctx->dmub_srv, &cmd);
3453     dc_dmub_srv_cmd_execute(pipe_ctx->stream->ctx->dmub_srv);
3454     dc_dmub_srv_wait_idle(pipe_ctx->stream->ctx->dmub_srv);
3455 }
3456 
3457 void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx)
3458 {
3459     struct dc_cursor_position pos_cpy = pipe_ctx->stream->cursor_position;
3460     struct hubp *hubp = pipe_ctx->plane_res.hubp;
3461     struct dpp *dpp = pipe_ctx->plane_res.dpp;
3462     struct dc_cursor_mi_param param = {
3463         .pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10,
3464         .ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz,
3465         .viewport = pipe_ctx->plane_res.scl_data.viewport,
3466         .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz,
3467         .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert,
3468         .rotation = pipe_ctx->plane_state->rotation,
3469         .mirror = pipe_ctx->plane_state->horizontal_mirror
3470     };
3471     bool pipe_split_on = (pipe_ctx->top_pipe != NULL) ||
3472         (pipe_ctx->bottom_pipe != NULL);
3473     bool odm_combine_on = (pipe_ctx->next_odm_pipe != NULL) ||
3474         (pipe_ctx->prev_odm_pipe != NULL);
3475 
3476     int x_plane = pipe_ctx->plane_state->dst_rect.x;
3477     int y_plane = pipe_ctx->plane_state->dst_rect.y;
3478     int x_pos = pos_cpy.x;
3479     int y_pos = pos_cpy.y;
3480 
3481     /**
3482      * DC cursor is stream space, HW cursor is plane space and drawn
3483      * as part of the framebuffer.
3484      *
3485      * Cursor position can't be negative, but hotspot can be used to
3486      * shift cursor out of the plane bounds. Hotspot must be smaller
3487      * than the cursor size.
3488      */
3489 
3490     /**
3491      * Translate cursor from stream space to plane space.
3492      *
3493      * If the cursor is scaled then we need to scale the position
3494      * to be in the approximately correct place. We can't do anything
3495      * about the actual size being incorrect, that's a limitation of
3496      * the hardware.
3497      */
3498     if (param.rotation == ROTATION_ANGLE_90 || param.rotation == ROTATION_ANGLE_270) {
3499         x_pos = (x_pos - x_plane) * pipe_ctx->plane_state->src_rect.height /
3500                 pipe_ctx->plane_state->dst_rect.width;
3501         y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.width /
3502                 pipe_ctx->plane_state->dst_rect.height;
3503     } else {
3504         x_pos = (x_pos - x_plane) * pipe_ctx->plane_state->src_rect.width /
3505                 pipe_ctx->plane_state->dst_rect.width;
3506         y_pos = (y_pos - y_plane) * pipe_ctx->plane_state->src_rect.height /
3507                 pipe_ctx->plane_state->dst_rect.height;
3508     }
3509 
3510     /**
3511      * If the cursor's source viewport is clipped then we need to
3512      * translate the cursor to appear in the correct position on
3513      * the screen.
3514      *
3515      * This translation isn't affected by scaling so it needs to be
3516      * done *after* we adjust the position for the scale factor.
3517      *
3518      * This is only done by opt-in for now since there are still
3519      * some usecases like tiled display that might enable the
3520      * cursor on both streams while expecting dc to clip it.
3521      */
3522     if (pos_cpy.translate_by_source) {
3523         x_pos += pipe_ctx->plane_state->src_rect.x;
3524         y_pos += pipe_ctx->plane_state->src_rect.y;
3525     }
3526 
3527     /**
3528      * If the position is negative then we need to add to the hotspot
3529      * to shift the cursor outside the plane.
3530      */
3531 
3532     if (x_pos < 0) {
3533         pos_cpy.x_hotspot -= x_pos;
3534         x_pos = 0;
3535     }
3536 
3537     if (y_pos < 0) {
3538         pos_cpy.y_hotspot -= y_pos;
3539         y_pos = 0;
3540     }
3541 
3542     pos_cpy.x = (uint32_t)x_pos;
3543     pos_cpy.y = (uint32_t)y_pos;
3544 
3545     if (pipe_ctx->plane_state->address.type
3546             == PLN_ADDR_TYPE_VIDEO_PROGRESSIVE)
3547         pos_cpy.enable = false;
3548 
3549     if (pos_cpy.enable && dcn10_can_pipe_disable_cursor(pipe_ctx))
3550         pos_cpy.enable = false;
3551 
3552     // Swap axis and mirror horizontally
3553     if (param.rotation == ROTATION_ANGLE_90) {
3554         uint32_t temp_x = pos_cpy.x;
3555 
3556         pos_cpy.x = pipe_ctx->plane_res.scl_data.viewport.width -
3557                 (pos_cpy.y - pipe_ctx->plane_res.scl_data.viewport.x) + pipe_ctx->plane_res.scl_data.viewport.x;
3558         pos_cpy.y = temp_x;
3559     }
3560     // Swap axis and mirror vertically
3561     else if (param.rotation == ROTATION_ANGLE_270) {
3562         uint32_t temp_y = pos_cpy.y;
3563         int viewport_height =
3564             pipe_ctx->plane_res.scl_data.viewport.height;
3565         int viewport_y =
3566             pipe_ctx->plane_res.scl_data.viewport.y;
3567 
3568         /**
3569          * Display groups that are 1xnY, have pos_cpy.x > 2 * viewport.height
3570          * For pipe split cases:
3571          * - apply offset of viewport.y to normalize pos_cpy.x
3572          * - calculate the pos_cpy.y as before
3573          * - shift pos_cpy.y back by same offset to get final value
3574          * - since we iterate through both pipes, use the lower
3575          *   viewport.y for offset
3576          * For non pipe split cases, use the same calculation for
3577          *  pos_cpy.y as the 180 degree rotation case below,
3578          *  but use pos_cpy.x as our input because we are rotating
3579          *  270 degrees
3580          */
3581         if (pipe_split_on || odm_combine_on) {
3582             int pos_cpy_x_offset;
3583             int other_pipe_viewport_y;
3584 
3585             if (pipe_split_on) {
3586                 if (pipe_ctx->bottom_pipe) {
3587                     other_pipe_viewport_y =
3588                         pipe_ctx->bottom_pipe->plane_res.scl_data.viewport.y;
3589                 } else {
3590                     other_pipe_viewport_y =
3591                         pipe_ctx->top_pipe->plane_res.scl_data.viewport.y;
3592                 }
3593             } else {
3594                 if (pipe_ctx->next_odm_pipe) {
3595                     other_pipe_viewport_y =
3596                         pipe_ctx->next_odm_pipe->plane_res.scl_data.viewport.y;
3597                 } else {
3598                     other_pipe_viewport_y =
3599                         pipe_ctx->prev_odm_pipe->plane_res.scl_data.viewport.y;
3600                 }
3601             }
3602             pos_cpy_x_offset = (viewport_y > other_pipe_viewport_y) ?
3603                 other_pipe_viewport_y : viewport_y;
3604             pos_cpy.x -= pos_cpy_x_offset;
3605             if (pos_cpy.x > viewport_height) {
3606                 pos_cpy.x = pos_cpy.x - viewport_height;
3607                 pos_cpy.y = viewport_height - pos_cpy.x;
3608             } else {
3609                 pos_cpy.y = 2 * viewport_height - pos_cpy.x;
3610             }
3611             pos_cpy.y += pos_cpy_x_offset;
3612         } else {
3613             pos_cpy.y = (2 * viewport_y) + viewport_height - pos_cpy.x;
3614         }
3615         pos_cpy.x = temp_y;
3616     }
3617     // Mirror horizontally and vertically
3618     else if (param.rotation == ROTATION_ANGLE_180) {
3619         int viewport_width =
3620             pipe_ctx->plane_res.scl_data.viewport.width;
3621         int viewport_x =
3622             pipe_ctx->plane_res.scl_data.viewport.x;
3623 
3624         if (pipe_split_on || odm_combine_on) {
3625             if (pos_cpy.x >= viewport_width + viewport_x) {
3626                 pos_cpy.x = 2 * viewport_width
3627                         - pos_cpy.x + 2 * viewport_x;
3628             } else {
3629                 uint32_t temp_x = pos_cpy.x;
3630 
3631                 pos_cpy.x = 2 * viewport_x - pos_cpy.x;
3632                 if (temp_x >= viewport_x +
3633                     (int)hubp->curs_attr.width || pos_cpy.x
3634                     <= (int)hubp->curs_attr.width +
3635                     pipe_ctx->plane_state->src_rect.x) {
3636                     pos_cpy.x = temp_x + viewport_width;
3637                 }
3638             }
3639         } else {
3640             pos_cpy.x = viewport_width - pos_cpy.x + 2 * viewport_x;
3641         }
3642 
3643         /**
3644          * Display groups that are 1xnY, have pos_cpy.y > viewport.height
3645          * Calculation:
3646          *   delta_from_bottom = viewport.y + viewport.height - pos_cpy.y
3647          *   pos_cpy.y_new = viewport.y + delta_from_bottom
3648          * Simplify it as:
3649          *   pos_cpy.y = viewport.y * 2 + viewport.height - pos_cpy.y
3650          */
3651         pos_cpy.y = (2 * pipe_ctx->plane_res.scl_data.viewport.y) +
3652             pipe_ctx->plane_res.scl_data.viewport.height - pos_cpy.y;
3653     }
3654 
3655     dcn10_dmub_update_cursor_data(pipe_ctx, hubp, &param, &pos_cpy, NULL);
3656     hubp->funcs->set_cursor_position(hubp, &pos_cpy, &param);
3657     dpp->funcs->set_cursor_position(dpp, &pos_cpy, &param, hubp->curs_attr.width, hubp->curs_attr.height);
3658 }
3659 
3660 void dcn10_set_cursor_attribute(struct pipe_ctx *pipe_ctx)
3661 {
3662     struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes;
3663     struct dc_cursor_mi_param param = { 0 };
3664 
3665     /**
3666      * If enter PSR without cursor attribute update
3667      * the cursor attribute of dmub_restore_plane
3668      * are initial value. call dmub to exit PSR and
3669      * restore plane then update cursor attribute to
3670      * avoid override with initial value
3671      */
3672     if (pipe_ctx->plane_state != NULL) {
3673         param.pixel_clk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10;
3674         param.ref_clk_khz = pipe_ctx->stream->ctx->dc->res_pool->ref_clocks.dchub_ref_clock_inKhz;
3675         param.viewport = pipe_ctx->plane_res.scl_data.viewport;
3676         param.h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz;
3677         param.v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert;
3678         param.rotation = pipe_ctx->plane_state->rotation;
3679         param.mirror = pipe_ctx->plane_state->horizontal_mirror;
3680         dcn10_dmub_update_cursor_data(pipe_ctx, pipe_ctx->plane_res.hubp, &param, NULL, attributes);
3681     }
3682 
3683     pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes(
3684             pipe_ctx->plane_res.hubp, attributes);
3685     pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes(
3686         pipe_ctx->plane_res.dpp, attributes);
3687 }
3688 
3689 void dcn10_set_cursor_sdr_white_level(struct pipe_ctx *pipe_ctx)
3690 {
3691     uint32_t sdr_white_level = pipe_ctx->stream->cursor_attributes.sdr_white_level;
3692     struct fixed31_32 multiplier;
3693     struct dpp_cursor_attributes opt_attr = { 0 };
3694     uint32_t hw_scale = 0x3c00; // 1.0 default multiplier
3695     struct custom_float_format fmt;
3696 
3697     if (!pipe_ctx->plane_res.dpp->funcs->set_optional_cursor_attributes)
3698         return;
3699 
3700     fmt.exponenta_bits = 5;
3701     fmt.mantissa_bits = 10;
3702     fmt.sign = true;
3703 
3704     if (sdr_white_level > 80) {
3705         multiplier = dc_fixpt_from_fraction(sdr_white_level, 80);
3706         convert_to_custom_float_format(multiplier, &fmt, &hw_scale);
3707     }
3708 
3709     opt_attr.scale = hw_scale;
3710     opt_attr.bias = 0;
3711 
3712     pipe_ctx->plane_res.dpp->funcs->set_optional_cursor_attributes(
3713             pipe_ctx->plane_res.dpp, &opt_attr);
3714 }
3715 
3716 /*
3717  * apply_front_porch_workaround  TODO FPGA still need?
3718  *
3719  * This is a workaround for a bug that has existed since R5xx and has not been
3720  * fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive.
3721  */
3722 static void apply_front_porch_workaround(
3723     struct dc_crtc_timing *timing)
3724 {
3725     if (timing->flags.INTERLACE == 1) {
3726         if (timing->v_front_porch < 2)
3727             timing->v_front_porch = 2;
3728     } else {
3729         if (timing->v_front_porch < 1)
3730             timing->v_front_porch = 1;
3731     }
3732 }
3733 
3734 int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx)
3735 {
3736     const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing;
3737     struct dc_crtc_timing patched_crtc_timing;
3738     int vesa_sync_start;
3739     int asic_blank_end;
3740     int interlace_factor;
3741     int vertical_line_start;
3742 
3743     patched_crtc_timing = *dc_crtc_timing;
3744     apply_front_porch_workaround(&patched_crtc_timing);
3745 
3746     interlace_factor = patched_crtc_timing.flags.INTERLACE ? 2 : 1;
3747 
3748     vesa_sync_start = patched_crtc_timing.v_addressable +
3749             patched_crtc_timing.v_border_bottom +
3750             patched_crtc_timing.v_front_porch;
3751 
3752     asic_blank_end = (patched_crtc_timing.v_total -
3753             vesa_sync_start -
3754             patched_crtc_timing.v_border_top)
3755             * interlace_factor;
3756 
3757     vertical_line_start = asic_blank_end -
3758             pipe_ctx->pipe_dlg_param.vstartup_start + 1;
3759 
3760     return vertical_line_start;
3761 }
3762 
3763 void dcn10_calc_vupdate_position(
3764         struct dc *dc,
3765         struct pipe_ctx *pipe_ctx,
3766         uint32_t *start_line,
3767         uint32_t *end_line)
3768 {
3769     const struct dc_crtc_timing *dc_crtc_timing = &pipe_ctx->stream->timing;
3770     int vline_int_offset_from_vupdate =
3771             pipe_ctx->stream->periodic_interrupt0.lines_offset;
3772     int vupdate_offset_from_vsync = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
3773     int start_position;
3774 
3775     if (vline_int_offset_from_vupdate > 0)
3776         vline_int_offset_from_vupdate--;
3777     else if (vline_int_offset_from_vupdate < 0)
3778         vline_int_offset_from_vupdate++;
3779 
3780     start_position = vline_int_offset_from_vupdate + vupdate_offset_from_vsync;
3781 
3782     if (start_position >= 0)
3783         *start_line = start_position;
3784     else
3785         *start_line = dc_crtc_timing->v_total + start_position - 1;
3786 
3787     *end_line = *start_line + 2;
3788 
3789     if (*end_line >= dc_crtc_timing->v_total)
3790         *end_line = 2;
3791 }
3792 
3793 static void dcn10_cal_vline_position(
3794         struct dc *dc,
3795         struct pipe_ctx *pipe_ctx,
3796         enum vline_select vline,
3797         uint32_t *start_line,
3798         uint32_t *end_line)
3799 {
3800     enum vertical_interrupt_ref_point ref_point = INVALID_POINT;
3801 
3802     if (vline == VLINE0)
3803         ref_point = pipe_ctx->stream->periodic_interrupt0.ref_point;
3804     else if (vline == VLINE1)
3805         ref_point = pipe_ctx->stream->periodic_interrupt1.ref_point;
3806 
3807     switch (ref_point) {
3808     case START_V_UPDATE:
3809         dcn10_calc_vupdate_position(
3810                 dc,
3811                 pipe_ctx,
3812                 start_line,
3813                 end_line);
3814         break;
3815     case START_V_SYNC:
3816         // Suppose to do nothing because vsync is 0;
3817         break;
3818     default:
3819         ASSERT(0);
3820         break;
3821     }
3822 }
3823 
3824 void dcn10_setup_periodic_interrupt(
3825         struct dc *dc,
3826         struct pipe_ctx *pipe_ctx,
3827         enum vline_select vline)
3828 {
3829     struct timing_generator *tg = pipe_ctx->stream_res.tg;
3830 
3831     if (vline == VLINE0) {
3832         uint32_t start_line = 0;
3833         uint32_t end_line = 0;
3834 
3835         dcn10_cal_vline_position(dc, pipe_ctx, vline, &start_line, &end_line);
3836 
3837         tg->funcs->setup_vertical_interrupt0(tg, start_line, end_line);
3838 
3839     } else if (vline == VLINE1) {
3840         pipe_ctx->stream_res.tg->funcs->setup_vertical_interrupt1(
3841                 tg,
3842                 pipe_ctx->stream->periodic_interrupt1.lines_offset);
3843     }
3844 }
3845 
3846 void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx)
3847 {
3848     struct timing_generator *tg = pipe_ctx->stream_res.tg;
3849     int start_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
3850 
3851     if (start_line < 0) {
3852         ASSERT(0);
3853         start_line = 0;
3854     }
3855 
3856     if (tg->funcs->setup_vertical_interrupt2)
3857         tg->funcs->setup_vertical_interrupt2(tg, start_line);
3858 }
3859 
3860 void dcn10_unblank_stream(struct pipe_ctx *pipe_ctx,
3861         struct dc_link_settings *link_settings)
3862 {
3863     struct encoder_unblank_param params = {0};
3864     struct dc_stream_state *stream = pipe_ctx->stream;
3865     struct dc_link *link = stream->link;
3866     struct dce_hwseq *hws = link->dc->hwseq;
3867 
3868     /* only 3 items below are used by unblank */
3869     params.timing = pipe_ctx->stream->timing;
3870 
3871     params.link_settings.link_rate = link_settings->link_rate;
3872 
3873     if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
3874         if (params.timing.pixel_encoding == PIXEL_ENCODING_YCBCR420)
3875             params.timing.pix_clk_100hz /= 2;
3876         pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, &params);
3877     }
3878 
3879     if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
3880         hws->funcs.edp_backlight_control(link, true);
3881     }
3882 }
3883 
3884 void dcn10_send_immediate_sdp_message(struct pipe_ctx *pipe_ctx,
3885                 const uint8_t *custom_sdp_message,
3886                 unsigned int sdp_message_size)
3887 {
3888     if (dc_is_dp_signal(pipe_ctx->stream->signal)) {
3889         pipe_ctx->stream_res.stream_enc->funcs->send_immediate_sdp_message(
3890                 pipe_ctx->stream_res.stream_enc,
3891                 custom_sdp_message,
3892                 sdp_message_size);
3893     }
3894 }
3895 enum dc_status dcn10_set_clock(struct dc *dc,
3896             enum dc_clock_type clock_type,
3897             uint32_t clk_khz,
3898             uint32_t stepping)
3899 {
3900     struct dc_state *context = dc->current_state;
3901     struct dc_clock_config clock_cfg = {0};
3902     struct dc_clocks *current_clocks = &context->bw_ctx.bw.dcn.clk;
3903 
3904     if (!dc->clk_mgr || !dc->clk_mgr->funcs->get_clock)
3905         return DC_FAIL_UNSUPPORTED_1;
3906 
3907     dc->clk_mgr->funcs->get_clock(dc->clk_mgr,
3908         context, clock_type, &clock_cfg);
3909 
3910     if (clk_khz > clock_cfg.max_clock_khz)
3911         return DC_FAIL_CLK_EXCEED_MAX;
3912 
3913     if (clk_khz < clock_cfg.min_clock_khz)
3914         return DC_FAIL_CLK_BELOW_MIN;
3915 
3916     if (clk_khz < clock_cfg.bw_requirequired_clock_khz)
3917         return DC_FAIL_CLK_BELOW_CFG_REQUIRED;
3918 
3919     /*update internal request clock for update clock use*/
3920     if (clock_type == DC_CLOCK_TYPE_DISPCLK)
3921         current_clocks->dispclk_khz = clk_khz;
3922     else if (clock_type == DC_CLOCK_TYPE_DPPCLK)
3923         current_clocks->dppclk_khz = clk_khz;
3924     else
3925         return DC_ERROR_UNEXPECTED;
3926 
3927     if (dc->clk_mgr->funcs->update_clocks)
3928                 dc->clk_mgr->funcs->update_clocks(dc->clk_mgr,
3929                 context, true);
3930     return DC_OK;
3931 
3932 }
3933 
3934 void dcn10_get_clock(struct dc *dc,
3935             enum dc_clock_type clock_type,
3936             struct dc_clock_config *clock_cfg)
3937 {
3938     struct dc_state *context = dc->current_state;
3939 
3940     if (dc->clk_mgr && dc->clk_mgr->funcs->get_clock)
3941                 dc->clk_mgr->funcs->get_clock(dc->clk_mgr, context, clock_type, clock_cfg);
3942 
3943 }
3944 
3945 void dcn10_get_dcc_en_bits(struct dc *dc, int *dcc_en_bits)
3946 {
3947     struct resource_pool *pool = dc->res_pool;
3948     int i;
3949 
3950     for (i = 0; i < pool->pipe_count; i++) {
3951         struct hubp *hubp = pool->hubps[i];
3952         struct dcn_hubp_state *s = &(TO_DCN10_HUBP(hubp)->state);
3953 
3954         hubp->funcs->hubp_read_state(hubp);
3955 
3956         if (!s->blank_en)
3957             dcc_en_bits[i] = s->dcc_en ? 1 : 0;
3958     }
3959 }