1 /* 2 * Copyright 2012-15 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 #include "reg_helper.h" 27 #include "dcn10_mpc.h" 28 #include "dc.h" 29 #include "mem_input.h" 30 31 #define REG(reg)\ 32 mpc10->mpc_regs->reg 33 34 #define CTX \ 35 mpc10->base.ctx 36 37 #undef FN 38 #define FN(reg_name, field_name) \ 39 mpc10->mpc_shift->field_name, mpc10->mpc_mask->field_name 40 41 #define MODE_TOP_ONLY 1 42 #define MODE_BLEND 3 43 #define BLND_PP_ALPHA 0 44 #define BLND_GLOBAL_ALPHA 2 45 46 47 static void mpc10_set_bg_color( 48 struct dcn10_mpc *mpc10, 49 struct tg_color *bg_color, 50 int id) 51 { 52 /* mpc color is 12 bit. tg_color is 10 bit */ 53 /* todo: might want to use 16 bit to represent color and have each 54 * hw block translate to correct color depth. 55 */ 56 uint32_t bg_r_cr = bg_color->color_r_cr << 2; 57 uint32_t bg_g_y = bg_color->color_g_y << 2; 58 uint32_t bg_b_cb = bg_color->color_b_cb << 2; 59 60 REG_SET(MPCC_BG_R_CR[id], 0, 61 MPCC_BG_R_CR, bg_r_cr); 62 REG_SET(MPCC_BG_G_Y[id], 0, 63 MPCC_BG_G_Y, bg_g_y); 64 REG_SET(MPCC_BG_B_CB[id], 0, 65 MPCC_BG_B_CB, bg_b_cb); 66 } 67 68 void mpc10_assert_idle_mpcc(struct mpc *mpc, int id) 69 { 70 struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); 71 72 ASSERT(!(mpc10->mpcc_in_use_mask & 1 << id)); 73 REG_WAIT(MPCC_STATUS[id], 74 MPCC_IDLE, 1, 75 1, 100000); 76 } 77 78 static int mpc10_get_idle_mpcc_id(struct dcn10_mpc *mpc10) 79 { 80 int i; 81 int last_free_mpcc_id = -1; 82 83 for (i = 0; i < mpc10->num_mpcc; i++) { 84 uint32_t is_idle = 0; 85 86 if (mpc10->mpcc_in_use_mask & 1 << i) 87 continue; 88 89 last_free_mpcc_id = i; 90 REG_GET(MPCC_STATUS[i], MPCC_IDLE, &is_idle); 91 if (is_idle) 92 return i; 93 } 94 95 /* This assert should never trigger, we have mpcc leak if it does */ 96 ASSERT(last_free_mpcc_id != -1); 97 98 mpc10_assert_idle_mpcc(&mpc10->base, last_free_mpcc_id); 99 return last_free_mpcc_id; 100 } 101 102 static void mpc10_assert_mpcc_idle_before_connect(struct dcn10_mpc *mpc10, int id) 103 { 104 unsigned int top_sel, mpc_busy, mpc_idle; 105 106 REG_GET(MPCC_TOP_SEL[id], 107 MPCC_TOP_SEL, &top_sel); 108 109 if (top_sel == 0xf) { 110 REG_GET_2(MPCC_STATUS[id], 111 MPCC_BUSY, &mpc_busy, 112 MPCC_IDLE, &mpc_idle); 113 114 ASSERT(mpc_busy == 0); 115 ASSERT(mpc_idle == 1); 116 } 117 } 118 119 void mpc10_mpcc_remove( 120 struct mpc *mpc, 121 struct mpc_tree_cfg *tree_cfg, 122 int opp_id, 123 int dpp_id) 124 { 125 struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); 126 int mpcc_id, z_idx; 127 128 /* find z_idx for the dpp to be removed */ 129 for (z_idx = 0; z_idx < tree_cfg->num_pipes; z_idx++) 130 if (tree_cfg->dpp[z_idx] == dpp_id) 131 break; 132 133 if (z_idx == tree_cfg->num_pipes) { 134 /* In case of resume from S3/S4, remove mpcc from bios left over */ 135 REG_SET(MPCC_OPP_ID[dpp_id], 0, 136 MPCC_OPP_ID, 0xf); 137 REG_SET(MPCC_TOP_SEL[dpp_id], 0, 138 MPCC_TOP_SEL, 0xf); 139 REG_SET(MPCC_BOT_SEL[dpp_id], 0, 140 MPCC_BOT_SEL, 0xf); 141 return; 142 } 143 144 mpcc_id = tree_cfg->mpcc[z_idx]; 145 146 REG_SET(MPCC_OPP_ID[mpcc_id], 0, 147 MPCC_OPP_ID, 0xf); 148 REG_SET(MPCC_TOP_SEL[mpcc_id], 0, 149 MPCC_TOP_SEL, 0xf); 150 REG_SET(MPCC_BOT_SEL[mpcc_id], 0, 151 MPCC_BOT_SEL, 0xf); 152 153 if (z_idx > 0) { 154 int top_mpcc_id = tree_cfg->mpcc[z_idx - 1]; 155 156 if (z_idx + 1 < tree_cfg->num_pipes) 157 /* mpcc to be removed is in the middle of the tree */ 158 REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0, 159 MPCC_BOT_SEL, tree_cfg->mpcc[z_idx + 1]); 160 else { 161 /* mpcc to be removed is at the bottom of the tree */ 162 REG_SET(MPCC_BOT_SEL[top_mpcc_id], 0, 163 MPCC_BOT_SEL, 0xf); 164 REG_UPDATE(MPCC_CONTROL[top_mpcc_id], 165 MPCC_MODE, MODE_TOP_ONLY); 166 } 167 } else if (tree_cfg->num_pipes > 1) 168 /* mpcc to be removed is at the top of the tree */ 169 REG_SET(MUX[opp_id], 0, 170 MPC_OUT_MUX, tree_cfg->mpcc[z_idx + 1]); 171 else 172 /* mpcc to be removed is the only one in the tree */ 173 REG_SET(MUX[opp_id], 0, MPC_OUT_MUX, 0xf); 174 175 /* mark this mpcc as not in use */ 176 mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id); 177 tree_cfg->num_pipes--; 178 for (; z_idx < tree_cfg->num_pipes; z_idx++) { 179 tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx + 1]; 180 tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx + 1]; 181 } 182 tree_cfg->dpp[tree_cfg->num_pipes] = 0xdeadbeef; 183 tree_cfg->mpcc[tree_cfg->num_pipes] = 0xdeadbeef; 184 } 185 186 static void mpc10_add_to_tree_cfg( 187 struct mpc *mpc, 188 struct mpcc_cfg *cfg, 189 int mpcc_id) 190 { 191 struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); 192 int mpcc_mode = MODE_TOP_ONLY; 193 int position = cfg->z_index; 194 struct mpc_tree_cfg *tree_cfg = cfg->tree_cfg; 195 int alpha_blnd_mode = cfg->per_pixel_alpha ? 196 BLND_PP_ALPHA : BLND_GLOBAL_ALPHA; 197 int z_idx; 198 199 REG_SET(MPCC_OPP_ID[mpcc_id], 0, 200 MPCC_OPP_ID, cfg->opp_id); 201 202 REG_SET(MPCC_TOP_SEL[mpcc_id], 0, 203 MPCC_TOP_SEL, cfg->dpp_id); 204 205 if (position == 0) { 206 /* idle dpp/mpcc is added to the top layer of tree */ 207 208 if (tree_cfg->num_pipes > 0) { 209 /* get instance of previous top mpcc */ 210 int prev_top_mpcc_id = tree_cfg->mpcc[0]; 211 212 REG_SET(MPCC_BOT_SEL[mpcc_id], 0, 213 MPCC_BOT_SEL, prev_top_mpcc_id); 214 mpcc_mode = MODE_BLEND; 215 } 216 217 /* opp will get new output. from new added mpcc */ 218 REG_SET(MUX[cfg->opp_id], 0, MPC_OUT_MUX, mpcc_id); 219 220 } else if (position == tree_cfg->num_pipes) { 221 /* idle dpp/mpcc is added to the bottom layer of tree */ 222 223 /* get instance of previous bottom mpcc, set to middle layer */ 224 int prev_bot_mpcc_id = tree_cfg->mpcc[tree_cfg->num_pipes - 1]; 225 226 REG_SET(MPCC_BOT_SEL[prev_bot_mpcc_id], 0, 227 MPCC_BOT_SEL, mpcc_id); 228 REG_UPDATE(MPCC_CONTROL[prev_bot_mpcc_id], 229 MPCC_MODE, MODE_BLEND); 230 231 /* mpcc_id become new bottom mpcc*/ 232 REG_SET(MPCC_BOT_SEL[mpcc_id], 0, 233 MPCC_BOT_SEL, 0xf); 234 235 } else { 236 /* idle dpp/mpcc is added to middle of tree */ 237 int above_mpcc_id = tree_cfg->mpcc[position - 1]; 238 int below_mpcc_id = tree_cfg->mpcc[position]; 239 240 /* mpcc above new mpcc_id has new bottom mux*/ 241 REG_SET(MPCC_BOT_SEL[above_mpcc_id], 0, 242 MPCC_BOT_SEL, mpcc_id); 243 REG_UPDATE(MPCC_CONTROL[above_mpcc_id], 244 MPCC_MODE, MODE_BLEND); 245 246 /* mpcc_id bottom mux is from below mpcc*/ 247 REG_SET(MPCC_BOT_SEL[mpcc_id], 0, 248 MPCC_BOT_SEL, below_mpcc_id); 249 mpcc_mode = MODE_BLEND; 250 } 251 252 REG_SET_4(MPCC_CONTROL[mpcc_id], 0xffffffff, 253 MPCC_MODE, mpcc_mode, 254 MPCC_ALPHA_BLND_MODE, alpha_blnd_mode, 255 MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha, 256 MPCC_BLND_ACTIVE_OVERLAP_ONLY, false); 257 258 /* update mpc_tree_cfg with new mpcc */ 259 for (z_idx = tree_cfg->num_pipes; z_idx > position; z_idx--) { 260 tree_cfg->dpp[z_idx] = tree_cfg->dpp[z_idx - 1]; 261 tree_cfg->mpcc[z_idx] = tree_cfg->mpcc[z_idx - 1]; 262 } 263 tree_cfg->dpp[position] = cfg->dpp_id; 264 tree_cfg->mpcc[position] = mpcc_id; 265 tree_cfg->num_pipes++; 266 } 267 268 int mpc10_mpcc_add(struct mpc *mpc, struct mpcc_cfg *cfg) 269 { 270 struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); 271 int mpcc_id, z_idx; 272 273 ASSERT(cfg->z_index < mpc10->num_mpcc); 274 275 /* check in dpp already exists in mpc tree */ 276 for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++) 277 if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id) 278 break; 279 if (z_idx == cfg->tree_cfg->num_pipes) { 280 ASSERT(cfg->z_index <= cfg->tree_cfg->num_pipes); 281 mpcc_id = mpc10_get_idle_mpcc_id(mpc10); 282 283 /* 284 * TODO: remove hack 285 * Note: currently there is a bug in init_hw such that 286 * on resume from hibernate, BIOS sets up MPCC0, and 287 * we do mpcc_remove but the mpcc cannot go to idle 288 * after remove. This cause us to pick mpcc1 here, 289 * which causes a pstate hang for yet unknown reason. 290 */ 291 mpcc_id = cfg->dpp_id; 292 /* end hack*/ 293 294 ASSERT(!(mpc10->mpcc_in_use_mask & 1 << mpcc_id)); 295 296 if (mpc->ctx->dc->debug.sanity_checks) 297 mpc10_assert_mpcc_idle_before_connect(mpc10, mpcc_id); 298 } else { 299 ASSERT(cfg->z_index < cfg->tree_cfg->num_pipes); 300 mpcc_id = cfg->tree_cfg->mpcc[z_idx]; 301 mpc10_mpcc_remove(mpc, cfg->tree_cfg, cfg->opp_id, cfg->dpp_id); 302 } 303 304 /* add dpp/mpcc pair to mpc_tree_cfg and update mpcc registers */ 305 mpc10_add_to_tree_cfg(mpc, cfg, mpcc_id); 306 307 /* set background color */ 308 mpc10_set_bg_color(mpc10, &cfg->black_color, mpcc_id); 309 310 /* mark this mpcc as in use */ 311 mpc10->mpcc_in_use_mask |= 1 << mpcc_id; 312 313 return mpcc_id; 314 } 315 316 void mpc10_update_blend_mode( 317 struct mpc *mpc, 318 struct mpcc_cfg *cfg) 319 { 320 struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc); 321 int mpcc_id, z_idx; 322 int alpha_blnd_mode = cfg->per_pixel_alpha ? 323 BLND_PP_ALPHA : BLND_GLOBAL_ALPHA; 324 325 /* find z_idx for the dpp that requires blending mode update*/ 326 for (z_idx = 0; z_idx < cfg->tree_cfg->num_pipes; z_idx++) 327 if (cfg->tree_cfg->dpp[z_idx] == cfg->dpp_id) 328 break; 329 330 ASSERT(z_idx < cfg->tree_cfg->num_pipes); 331 mpcc_id = cfg->tree_cfg->mpcc[z_idx]; 332 333 REG_UPDATE_2(MPCC_CONTROL[mpcc_id], 334 MPCC_ALPHA_BLND_MODE, alpha_blnd_mode, 335 MPCC_ALPHA_MULTIPLIED_MODE, cfg->pre_multiplied_alpha); 336 } 337 338 const struct mpc_funcs dcn10_mpc_funcs = { 339 .add = mpc10_mpcc_add, 340 .remove = mpc10_mpcc_remove, 341 .wait_for_idle = mpc10_assert_idle_mpcc, 342 .update_blend_mode = mpc10_update_blend_mode, 343 }; 344 345 void dcn10_mpc_construct(struct dcn10_mpc *mpc10, 346 struct dc_context *ctx, 347 const struct dcn_mpc_registers *mpc_regs, 348 const struct dcn_mpc_shift *mpc_shift, 349 const struct dcn_mpc_mask *mpc_mask, 350 int num_mpcc) 351 { 352 mpc10->base.ctx = ctx; 353 354 mpc10->base.funcs = &dcn10_mpc_funcs; 355 356 mpc10->mpc_regs = mpc_regs; 357 mpc10->mpc_shift = mpc_shift; 358 mpc10->mpc_mask = mpc_mask; 359 360 mpc10->mpcc_in_use_mask = 0; 361 mpc10->num_mpcc = num_mpcc; 362 } 363 364