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 29 #define REG(reg)\ 30 mpc->mpc_regs->reg 31 32 #define CTX \ 33 mpc->base.ctx 34 35 #undef FN 36 #define FN(reg_name, field_name) \ 37 mpc->mpc_shift->field_name, mpc->mpc_mask->field_name 38 39 /* Internal function to set mpc output mux */ 40 static void set_output_mux(struct dcn10_mpc *mpc, 41 uint8_t opp_id, 42 uint8_t mpcc_id) 43 { 44 if (mpcc_id != 0xf) 45 REG_UPDATE(OPP_PIPE_CONTROL[opp_id], 46 OPP_PIPE_CLOCK_EN, 1); 47 48 REG_SET(MUX[opp_id], 0, 49 MPC_OUT_MUX, mpcc_id); 50 51 /* TODO: Move to post when ready. 52 if (mpcc_id == 0xf) { 53 MPCC_REG_UPDATE(OPP_PIPE0_OPP_PIPE_CONTROL, 54 OPP_PIPE_CLOCK_EN, 0); 55 } 56 */ 57 } 58 59 static void set_blend_mode(struct dcn10_mpc *mpc, 60 enum blend_mode mode, 61 uint8_t mpcc_id) 62 { 63 /* Enable per-pixel alpha on this pipe */ 64 if (mode == TOP_BLND) 65 REG_UPDATE_3(MPCC_CONTROL[mpcc_id], 66 MPCC_ALPHA_BLND_MODE, 0, 67 MPCC_ALPHA_MULTIPLIED_MODE, 0, 68 MPCC_BLND_ACTIVE_OVERLAP_ONLY, 0); 69 else 70 REG_UPDATE_3(MPCC_CONTROL[mpcc_id], 71 MPCC_ALPHA_BLND_MODE, 0, 72 MPCC_ALPHA_MULTIPLIED_MODE, 1, 73 MPCC_BLND_ACTIVE_OVERLAP_ONLY, 1); 74 } 75 76 void dcn10_set_mpc_background_color(struct dcn10_mpc *mpc, 77 unsigned int mpcc_inst, 78 struct tg_color *bg_color) 79 { 80 /* mpc color is 12 bit. tg_color is 10 bit */ 81 /* todo: might want to use 16 bit to represent color and have each 82 * hw block translate to correct color depth. 83 */ 84 uint32_t bg_r_cr = bg_color->color_r_cr << 2; 85 uint32_t bg_g_y = bg_color->color_g_y << 2; 86 uint32_t bg_b_cb = bg_color->color_b_cb << 2; 87 88 REG_SET(MPCC_BG_R_CR[mpcc_inst], 0, 89 MPCC_BG_R_CR, bg_r_cr); 90 REG_SET(MPCC_BG_G_Y[mpcc_inst], 0, 91 MPCC_BG_G_Y, bg_g_y); 92 REG_SET(MPCC_BG_B_CB[mpcc_inst], 0, 93 MPCC_BG_B_CB, bg_b_cb); 94 } 95 96 /* This function programs MPC tree configuration 97 * Assume it is the initial time to setup MPC tree_configure, means 98 * the instance of dpp/mpcc/opp specified in structure tree_cfg are 99 * in idle status. 100 * Before invoke this function, ensure that master lock of OPTC specified 101 * by opp_id is set. 102 * 103 * tree_cfg[in] - new MPC_TREE_CFG 104 */ 105 106 void dcn10_set_mpc_tree(struct dcn10_mpc *mpc, 107 struct mpc_tree_cfg *tree_cfg) 108 { 109 int i; 110 111 for (i = 0; i < tree_cfg->num_pipes; i++) { 112 uint8_t mpcc_inst = tree_cfg->mpcc[i]; 113 114 REG_SET(MPCC_OPP_ID[mpcc_inst], 0, 115 MPCC_OPP_ID, tree_cfg->opp_id); 116 117 REG_SET(MPCC_TOP_SEL[mpcc_inst], 0, 118 MPCC_TOP_SEL, tree_cfg->dpp[i]); 119 120 if (i == tree_cfg->num_pipes-1) { 121 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 122 MPCC_BOT_SEL, 0xF); 123 124 /* MPCC_CONTROL->MPCC_MODE */ 125 REG_UPDATE(MPCC_CONTROL[mpcc_inst], 126 MPCC_MODE, tree_cfg->mode); 127 } else { 128 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 129 MPCC_BOT_SEL, tree_cfg->dpp[i+1]); 130 131 /* MPCC_CONTROL->MPCC_MODE */ 132 REG_UPDATE(MPCC_CONTROL[mpcc_inst], 133 MPCC_MODE, 3); 134 } 135 136 if (i == 0) 137 set_output_mux( 138 mpc, tree_cfg->opp_id, mpcc_inst); 139 140 set_blend_mode(mpc, tree_cfg->mode, mpcc_inst); 141 } 142 } 143 144 void dcn10_set_mpc_passthrough(struct dcn10_mpc *mpc, 145 uint8_t dpp_idx, 146 uint8_t mpcc_idx, 147 uint8_t opp_idx) 148 { 149 struct mpc_tree_cfg tree_cfg = { 0 }; 150 151 tree_cfg.num_pipes = 1; 152 tree_cfg.opp_id = opp_idx; 153 tree_cfg.mode = TOP_PASSTHRU; 154 /* TODO: FPGA bring up one MPC has only 1 DPP and 1 MPCC 155 * For blend case, need fill mode DPP and cascade MPCC 156 */ 157 tree_cfg.dpp[0] = dpp_idx; 158 tree_cfg.mpcc[0] = mpcc_idx; 159 dcn10_set_mpc_tree(mpc, &tree_cfg); 160 } 161 162 /* 163 * This is the function to remove current MPC tree specified by tree_cfg 164 * Before invoke this function, ensure that master lock of OPTC specified 165 * by opp_id is set. 166 * 167 *tree_cfg[in/out] - current MPC_TREE_CFG 168 */ 169 void dcn10_delete_mpc_tree(struct dcn10_mpc *mpc, 170 struct mpc_tree_cfg *tree_cfg) 171 { 172 int i; 173 174 for (i = 0; i < tree_cfg->num_pipes; i++) { 175 uint8_t mpcc_inst = tree_cfg->mpcc[i]; 176 177 REG_SET(MPCC_OPP_ID[mpcc_inst], 0, 178 MPCC_OPP_ID, 0xf); 179 180 REG_SET(MPCC_TOP_SEL[mpcc_inst], 0, 181 MPCC_TOP_SEL, 0xf); 182 183 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 184 MPCC_BOT_SEL, 0xF); 185 186 /* add remove dpp/mpcc pair into pending list 187 * TODO FPGA AddToPendingList if empty from pseudo code 188 */ 189 tree_cfg->dpp[i] = 0xf; 190 tree_cfg->mpcc[i] = 0xf; 191 } 192 set_output_mux(mpc, tree_cfg->opp_id, 0xf); 193 tree_cfg->opp_id = 0xf; 194 tree_cfg->num_pipes = 0; 195 } 196 197 /* TODO FPGA: how to handle DPP? 198 * Function to remove one of pipe from MPC configure tree by dpp idx 199 * Before invoke this function, ensure that master lock of OPTC specified 200 * by opp_id is set 201 * This function can be invoke multiple times to remove more than 1 dpps. 202 * 203 * tree_cfg[in/out] - current MPC_TREE_CFG 204 * idx[in] - index of dpp from tree_cfg to be removed. 205 */ 206 bool dcn10_remove_dpp(struct dcn10_mpc *mpc, 207 struct mpc_tree_cfg *tree_cfg, 208 uint8_t idx) 209 { 210 int i; 211 bool found = false; 212 213 /* find dpp_idx from dpp array of tree_cfg */ 214 for (i = 0; i < tree_cfg->num_pipes; i++) { 215 if (tree_cfg->dpp[i] == idx) { 216 found = true; 217 break; 218 } 219 } 220 221 if (found) { 222 /* add remove dpp/mpcc pair into pending list */ 223 224 /* TODO FPGA AddToPendingList if empty from pseudo code 225 * AddToPendingList(tree_cfg->dpp[i],tree_cfg->mpcc[i]); 226 */ 227 uint8_t mpcc_inst = tree_cfg->mpcc[i]; 228 229 REG_SET(MPCC_OPP_ID[mpcc_inst], 0, 230 MPCC_OPP_ID, 0xf); 231 232 REG_SET(MPCC_TOP_SEL[mpcc_inst], 0, 233 MPCC_TOP_SEL, 0xf); 234 235 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 236 MPCC_BOT_SEL, 0xF); 237 238 if (i == 0) { 239 if (tree_cfg->num_pipes > 1) 240 set_output_mux(mpc, 241 tree_cfg->opp_id, tree_cfg->mpcc[i+1]); 242 else 243 set_output_mux(mpc, tree_cfg->opp_id, 0xf); 244 } else if (i == tree_cfg->num_pipes-1) { 245 mpcc_inst = tree_cfg->mpcc[i - 1]; 246 247 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 248 MPCC_BOT_SEL, 0xF); 249 250 REG_UPDATE(MPCC_CONTROL[mpcc_inst], 251 MPCC_MODE, tree_cfg->mode); 252 } else { 253 mpcc_inst = tree_cfg->mpcc[i - 1]; 254 255 REG_SET(MPCC_BOT_SEL[mpcc_inst], 0, 256 MPCC_BOT_SEL, tree_cfg->mpcc[i+1]); 257 } 258 set_blend_mode(mpc, tree_cfg->mode, mpcc_inst); 259 260 /* update tree_cfg structure */ 261 while (i < tree_cfg->num_pipes - 1) { 262 tree_cfg->dpp[i] = tree_cfg->dpp[i+1]; 263 tree_cfg->mpcc[i] = tree_cfg->mpcc[i+1]; 264 i++; 265 } 266 tree_cfg->num_pipes--; 267 } 268 return found; 269 } 270 271 /* TODO FPGA: how to handle DPP? 272 * Function to add DPP/MPCC pair into MPC configure tree by position. 273 * Before invoke this function, ensure that master lock of OPTC specified 274 * by opp_id is set 275 * This function can be invoke multiple times to add more than 1 pipes. 276 * 277 * tree_cfg[in/out] - current MPC_TREE_CFG 278 * dpp_idx[in] - index of an idle dpp insatnce to be added. 279 * mpcc_idx[in] - index of an idle mpcc instance to be added. 280 * poistion[in] - position of dpp/mpcc pair to be added into current tree_cfg 281 * 0 means insert to the most top layer of MPC tree 282 */ 283 void dcn10_add_dpp(struct dcn10_mpc *mpc, 284 struct mpc_tree_cfg *tree_cfg, 285 uint8_t dpp_idx, 286 uint8_t mpcc_idx, 287 uint8_t position) 288 { 289 uint8_t temp; 290 uint8_t temp1; 291 292 REG_SET(MPCC_OPP_ID[mpcc_idx], 0, 293 MPCC_OPP_ID, tree_cfg->opp_id); 294 295 REG_SET(MPCC_TOP_SEL[mpcc_idx], 0, 296 MPCC_TOP_SEL, dpp_idx); 297 298 if (position == 0) { 299 /* idle dpp/mpcc is added to the top layer of tree */ 300 REG_SET(MPCC_BOT_SEL[mpcc_idx], 0, 301 MPCC_BOT_SEL, tree_cfg->mpcc[0]); 302 REG_UPDATE(MPCC_CONTROL[mpcc_idx], 303 MPCC_MODE, 3); 304 305 /* opp will get new output. from new added mpcc */ 306 set_output_mux(mpc, tree_cfg->opp_id, mpcc_idx); 307 308 set_blend_mode(mpc, tree_cfg->mode, mpcc_idx); 309 310 } else if (position == tree_cfg->num_pipes) { 311 /* idle dpp/mpcc is added to the bottom layer of tree */ 312 313 /* get instance of previous bottom mpcc, set to middle layer */ 314 temp = tree_cfg->mpcc[tree_cfg->num_pipes - 1]; 315 316 REG_SET(MPCC_BOT_SEL[temp], 0, 317 MPCC_BOT_SEL, mpcc_idx); 318 319 REG_UPDATE(MPCC_CONTROL[temp], 320 MPCC_MODE, 3); 321 322 /* mpcc_idx become new bottom mpcc*/ 323 REG_SET(MPCC_BOT_SEL[mpcc_idx], 0, 324 MPCC_BOT_SEL, 0xf); 325 326 REG_UPDATE(MPCC_CONTROL[mpcc_idx], 327 MPCC_MODE, tree_cfg->mode); 328 329 set_blend_mode(mpc, tree_cfg->mode, mpcc_idx); 330 } else { 331 /* idle dpp/mpcc is added to middle of tree */ 332 temp = tree_cfg->mpcc[position - 1]; 333 temp1 = tree_cfg->mpcc[position]; 334 335 /* new mpcc instance temp1 is added right after temp*/ 336 REG_SET(MPCC_BOT_SEL[temp], 0, 337 MPCC_BOT_SEL, mpcc_idx); 338 339 /* mpcc_idx connect previous temp+1 to new mpcc */ 340 REG_SET(MPCC_BOT_SEL[mpcc_idx], 0, 341 MPCC_BOT_SEL, temp1); 342 343 /* temp TODO: may not need*/ 344 REG_UPDATE(MPCC_CONTROL[temp], 345 MPCC_MODE, 3); 346 347 set_blend_mode(mpc, tree_cfg->mode, temp); 348 } 349 350 /* update tree_cfg structure */ 351 temp = tree_cfg->num_pipes - 1; 352 353 /* 354 * iterating from the last mpc/dpp pair to the one being added, shift 355 * them down one position 356 */ 357 while (temp > position) { 358 tree_cfg->dpp[temp + 1] = tree_cfg->dpp[temp]; 359 tree_cfg->mpcc[temp + 1] = tree_cfg->mpcc[temp]; 360 temp--; 361 } 362 363 /* insert the new mpc/dpp pair into the tree_cfg*/ 364 tree_cfg->dpp[position] = dpp_idx; 365 tree_cfg->mpcc[position] = mpcc_idx; 366 tree_cfg->num_pipes++; 367 } 368 369 void wait_mpcc_idle(struct dcn10_mpc *mpc, 370 uint8_t mpcc_id) 371 { 372 REG_WAIT(MPCC_STATUS[mpcc_id], 373 MPCC_IDLE, 1, 374 1000, 1000); 375 } 376 377