| /* |
| * Copyright 2016 Advanced Micro Devices, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: AMD |
| * |
| */ |
| |
| #include "dm_services.h" |
| #include "basics/dc_common.h" |
| #include "core_types.h" |
| #include "resource.h" |
| #include "dcn201_hwseq.h" |
| #include "dcn201/dcn201_optc.h" |
| #include "dce/dce_hwseq.h" |
| #include "hubp.h" |
| #include "dchubbub.h" |
| #include "timing_generator.h" |
| #include "opp.h" |
| #include "ipp.h" |
| #include "mpc.h" |
| #include "dccg.h" |
| #include "clk_mgr.h" |
| #include "reg_helper.h" |
| |
| #define CTX \ |
| hws->ctx |
| |
| #define REG(reg)\ |
| hws->regs->reg |
| |
| #define DC_LOGGER \ |
| dc->ctx->logger |
| |
| #undef FN |
| #define FN(reg_name, field_name) \ |
| hws->shifts->field_name, hws->masks->field_name |
| |
| static bool patch_address_for_sbs_tb_stereo( |
| struct pipe_ctx *pipe_ctx, PHYSICAL_ADDRESS_LOC *addr) |
| { |
| struct dc_plane_state *plane_state = pipe_ctx->plane_state; |
| bool sec_split = pipe_ctx->top_pipe && |
| pipe_ctx->top_pipe->plane_state == pipe_ctx->plane_state; |
| |
| if (sec_split && plane_state->address.type == PLN_ADDR_TYPE_GRPH_STEREO && |
| (pipe_ctx->stream->timing.timing_3d_format == |
| TIMING_3D_FORMAT_SIDE_BY_SIDE || |
| pipe_ctx->stream->timing.timing_3d_format == |
| TIMING_3D_FORMAT_TOP_AND_BOTTOM)) { |
| *addr = plane_state->address.grph_stereo.left_addr; |
| plane_state->address.grph_stereo.left_addr = |
| plane_state->address.grph_stereo.right_addr; |
| return true; |
| } else { |
| if (pipe_ctx->stream->view_format != VIEW_3D_FORMAT_NONE && |
| plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO) { |
| plane_state->address.type = PLN_ADDR_TYPE_GRPH_STEREO; |
| plane_state->address.grph_stereo.right_addr = |
| plane_state->address.grph_stereo.left_addr; |
| plane_state->address.grph_stereo.right_meta_addr = |
| plane_state->address.grph_stereo.left_meta_addr; |
| } |
| } |
| return false; |
| } |
| |
| static bool gpu_addr_to_uma(struct dce_hwseq *hwseq, |
| PHYSICAL_ADDRESS_LOC *addr) |
| { |
| bool is_in_uma; |
| |
| if (hwseq->fb_base.quad_part <= addr->quad_part && |
| addr->quad_part < hwseq->fb_top.quad_part) { |
| addr->quad_part -= hwseq->fb_base.quad_part; |
| addr->quad_part += hwseq->fb_offset.quad_part; |
| is_in_uma = true; |
| } else if (hwseq->fb_offset.quad_part <= addr->quad_part && |
| addr->quad_part <= hwseq->uma_top.quad_part) { |
| is_in_uma = true; |
| } else if (addr->quad_part == 0) { |
| is_in_uma = false; |
| } else { |
| is_in_uma = false; |
| BREAK_TO_DEBUGGER(); |
| } |
| return is_in_uma; |
| } |
| |
| static void plane_address_in_gpu_space_to_uma(struct dce_hwseq *hwseq, |
| struct dc_plane_address *addr) |
| { |
| switch (addr->type) { |
| case PLN_ADDR_TYPE_GRAPHICS: |
| gpu_addr_to_uma(hwseq, &addr->grph.addr); |
| gpu_addr_to_uma(hwseq, &addr->grph.meta_addr); |
| break; |
| case PLN_ADDR_TYPE_GRPH_STEREO: |
| gpu_addr_to_uma(hwseq, &addr->grph_stereo.left_addr); |
| gpu_addr_to_uma(hwseq, &addr->grph_stereo.left_meta_addr); |
| gpu_addr_to_uma(hwseq, &addr->grph_stereo.right_addr); |
| gpu_addr_to_uma(hwseq, &addr->grph_stereo.right_meta_addr); |
| break; |
| case PLN_ADDR_TYPE_VIDEO_PROGRESSIVE: |
| gpu_addr_to_uma(hwseq, &addr->video_progressive.luma_addr); |
| gpu_addr_to_uma(hwseq, &addr->video_progressive.luma_meta_addr); |
| gpu_addr_to_uma(hwseq, &addr->video_progressive.chroma_addr); |
| gpu_addr_to_uma(hwseq, &addr->video_progressive.chroma_meta_addr); |
| break; |
| default: |
| BREAK_TO_DEBUGGER(); |
| break; |
| } |
| } |
| |
| void dcn201_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) |
| { |
| bool addr_patched = false; |
| PHYSICAL_ADDRESS_LOC addr; |
| struct dc_plane_state *plane_state = pipe_ctx->plane_state; |
| struct dce_hwseq *hws = dc->hwseq; |
| struct dc_plane_address uma; |
| |
| if (plane_state == NULL) |
| return; |
| |
| uma = plane_state->address; |
| addr_patched = patch_address_for_sbs_tb_stereo(pipe_ctx, &addr); |
| |
| plane_address_in_gpu_space_to_uma(hws, &uma); |
| |
| pipe_ctx->plane_res.hubp->funcs->hubp_program_surface_flip_and_addr( |
| pipe_ctx->plane_res.hubp, |
| &uma, |
| plane_state->flip_immediate); |
| |
| plane_state->status.requested_address = plane_state->address; |
| |
| if (plane_state->flip_immediate) |
| plane_state->status.current_address = plane_state->address; |
| |
| if (addr_patched) |
| pipe_ctx->plane_state->address.grph_stereo.left_addr = addr; |
| } |
| |
| /* Blank pixel data during initialization */ |
| void dcn201_init_blank( |
| struct dc *dc, |
| struct timing_generator *tg) |
| { |
| struct dce_hwseq *hws = dc->hwseq; |
| enum dc_color_space color_space; |
| struct tg_color black_color = {0}; |
| struct output_pixel_processor *opp = NULL; |
| uint32_t num_opps, opp_id_src0, opp_id_src1; |
| uint32_t otg_active_width = 0, otg_active_height = 0; |
| |
| /* program opp dpg blank color */ |
| color_space = COLOR_SPACE_SRGB; |
| color_space_to_black_color(dc, color_space, &black_color); |
| |
| /* get the OTG active size */ |
| tg->funcs->get_otg_active_size(tg, |
| &otg_active_width, |
| &otg_active_height); |
| |
| /* get the OPTC source */ |
| tg->funcs->get_optc_source(tg, &num_opps, &opp_id_src0, &opp_id_src1); |
| ASSERT(opp_id_src0 < dc->res_pool->res_cap->num_opp); |
| opp = dc->res_pool->opps[opp_id_src0]; |
| |
| opp->funcs->opp_set_disp_pattern_generator( |
| opp, |
| CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR, |
| CONTROLLER_DP_COLOR_SPACE_UDEFINED, |
| COLOR_DEPTH_UNDEFINED, |
| &black_color, |
| otg_active_width, |
| otg_active_height, |
| 0); |
| |
| hws->funcs.wait_for_blank_complete(opp); |
| } |
| |
| static void read_mmhub_vm_setup(struct dce_hwseq *hws) |
| { |
| uint32_t fb_base = REG_READ(MC_VM_FB_LOCATION_BASE); |
| uint32_t fb_top = REG_READ(MC_VM_FB_LOCATION_TOP); |
| uint32_t fb_offset = REG_READ(MC_VM_FB_OFFSET); |
| |
| /* MC_VM_FB_LOCATION_TOP is in pages, actual top should add 1 */ |
| fb_top++; |
| |
| /* bit 23:0 in register map to bit 47:24 in address */ |
| hws->fb_base.low_part = fb_base; |
| hws->fb_base.quad_part <<= 24; |
| |
| hws->fb_top.low_part = fb_top; |
| hws->fb_top.quad_part <<= 24; |
| hws->fb_offset.low_part = fb_offset; |
| hws->fb_offset.quad_part <<= 24; |
| |
| hws->uma_top.quad_part = hws->fb_top.quad_part |
| - hws->fb_base.quad_part + hws->fb_offset.quad_part; |
| } |
| |
| void dcn201_init_hw(struct dc *dc) |
| { |
| int i, j; |
| struct dce_hwseq *hws = dc->hwseq; |
| struct resource_pool *res_pool = dc->res_pool; |
| struct dc_state *context = dc->current_state; |
| |
| if (res_pool->dccg->funcs->dccg_init) |
| res_pool->dccg->funcs->dccg_init(res_pool->dccg); |
| |
| if (dc->clk_mgr && dc->clk_mgr->funcs->init_clocks) |
| dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); |
| |
| hws->funcs.bios_golden_init(dc); |
| |
| if (dc->ctx->dc_bios->fw_info_valid) { |
| res_pool->ref_clocks.xtalin_clock_inKhz = |
| dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency; |
| |
| if (res_pool->hubbub) { |
| (res_pool->dccg->funcs->get_dccg_ref_freq)(res_pool->dccg, |
| dc->ctx->dc_bios->fw_info.pll_info.crystal_frequency, |
| &res_pool->ref_clocks.dccg_ref_clock_inKhz); |
| |
| (res_pool->hubbub->funcs->get_dchub_ref_freq)(res_pool->hubbub, |
| res_pool->ref_clocks.dccg_ref_clock_inKhz, |
| &res_pool->ref_clocks.dchub_ref_clock_inKhz); |
| } else { |
| res_pool->ref_clocks.dccg_ref_clock_inKhz = |
| res_pool->ref_clocks.xtalin_clock_inKhz; |
| res_pool->ref_clocks.dchub_ref_clock_inKhz = |
| res_pool->ref_clocks.xtalin_clock_inKhz; |
| } |
| } else |
| ASSERT_CRITICAL(false); |
| for (i = 0; i < dc->link_count; i++) { |
| /* Power up AND update implementation according to the |
| * required signal (which may be different from the |
| * default signal on connector). |
| */ |
| struct dc_link *link = dc->links[i]; |
| |
| link->link_enc->funcs->hw_init(link->link_enc); |
| } |
| if (hws->fb_offset.quad_part == 0) |
| read_mmhub_vm_setup(hws); |
| |
| /* Blank pixel data with OPP DPG */ |
| for (i = 0; i < res_pool->timing_generator_count; i++) { |
| struct timing_generator *tg = res_pool->timing_generators[i]; |
| |
| if (tg->funcs->is_tg_enabled(tg)) { |
| dcn201_init_blank(dc, tg); |
| } |
| } |
| |
| for (i = 0; i < res_pool->timing_generator_count; i++) { |
| struct timing_generator *tg = res_pool->timing_generators[i]; |
| |
| if (tg->funcs->is_tg_enabled(tg)) |
| tg->funcs->lock(tg); |
| } |
| |
| for (i = 0; i < res_pool->pipe_count; i++) { |
| struct dpp *dpp = res_pool->dpps[i]; |
| |
| dpp->funcs->dpp_reset(dpp); |
| } |
| |
| /* Reset all MPCC muxes */ |
| res_pool->mpc->funcs->mpc_init(res_pool->mpc); |
| |
| /* initialize OPP mpc_tree parameter */ |
| for (i = 0; i < res_pool->res_cap->num_opp; i++) { |
| res_pool->opps[i]->mpc_tree_params.opp_id = res_pool->opps[i]->inst; |
| res_pool->opps[i]->mpc_tree_params.opp_list = NULL; |
| for (j = 0; j < MAX_PIPES; j++) |
| res_pool->opps[i]->mpcc_disconnect_pending[j] = false; |
| } |
| |
| for (i = 0; i < res_pool->timing_generator_count; i++) { |
| struct timing_generator *tg = res_pool->timing_generators[i]; |
| struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
| struct hubp *hubp = res_pool->hubps[i]; |
| struct dpp *dpp = res_pool->dpps[i]; |
| |
| pipe_ctx->stream_res.tg = tg; |
| pipe_ctx->pipe_idx = i; |
| |
| pipe_ctx->plane_res.hubp = hubp; |
| pipe_ctx->plane_res.dpp = dpp; |
| pipe_ctx->plane_res.mpcc_inst = dpp->inst; |
| hubp->mpcc_id = dpp->inst; |
| hubp->opp_id = OPP_ID_INVALID; |
| hubp->power_gated = false; |
| pipe_ctx->stream_res.opp = NULL; |
| |
| hubp->funcs->hubp_init(hubp); |
| |
| res_pool->opps[i]->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; |
| pipe_ctx->stream_res.opp = res_pool->opps[i]; |
| /*To do: number of MPCC != number of opp*/ |
| hws->funcs.plane_atomic_disconnect(dc, context, pipe_ctx); |
| } |
| |
| /* initialize DWB pointer to MCIF_WB */ |
| for (i = 0; i < res_pool->res_cap->num_dwb; i++) |
| res_pool->dwbc[i]->mcif = res_pool->mcif_wb[i]; |
| |
| for (i = 0; i < res_pool->timing_generator_count; i++) { |
| struct timing_generator *tg = res_pool->timing_generators[i]; |
| |
| if (tg->funcs->is_tg_enabled(tg)) |
| tg->funcs->unlock(tg); |
| } |
| |
| for (i = 0; i < res_pool->pipe_count; i++) { |
| struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
| |
| dc->hwss.disable_plane(dc, context, pipe_ctx); |
| |
| pipe_ctx->stream_res.tg = NULL; |
| pipe_ctx->plane_res.hubp = NULL; |
| } |
| |
| for (i = 0; i < res_pool->timing_generator_count; i++) { |
| struct timing_generator *tg = res_pool->timing_generators[i]; |
| |
| tg->funcs->tg_init(tg); |
| } |
| |
| for (i = 0; i < res_pool->audio_count; i++) { |
| struct audio *audio = res_pool->audios[i]; |
| |
| audio->funcs->hw_init(audio); |
| } |
| |
| /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ |
| REG_WRITE(DIO_MEM_PWR_CTRL, 0); |
| |
| if (!dc->debug.disable_clock_gate) { |
| /* enable all DCN clock gating */ |
| REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); |
| |
| REG_WRITE(DCCG_GATE_DISABLE_CNTL2, 0); |
| |
| REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); |
| } |
| } |
| |
| /* trigger HW to start disconnect plane from stream on the next vsync */ |
| void dcn201_plane_atomic_disconnect(struct dc *dc, |
| struct dc_state *state, |
| struct pipe_ctx *pipe_ctx) |
| { |
| struct dce_hwseq *hws = dc->hwseq; |
| struct hubp *hubp = pipe_ctx->plane_res.hubp; |
| int dpp_id = pipe_ctx->plane_res.dpp->inst; |
| struct mpc *mpc = dc->res_pool->mpc; |
| struct mpc_tree *mpc_tree_params; |
| struct mpcc *mpcc_to_remove = NULL; |
| struct output_pixel_processor *opp = pipe_ctx->stream_res.opp; |
| bool mpcc_removed = false; |
| |
| mpc_tree_params = &(opp->mpc_tree_params); |
| |
| /* check if this plane is being used by an MPCC in the secondary blending chain */ |
| if (mpc->funcs->get_mpcc_for_dpp_from_secondary) |
| mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp_from_secondary(mpc_tree_params, dpp_id); |
| |
| /* remove MPCC from secondary if being used */ |
| if (mpcc_to_remove != NULL && mpc->funcs->remove_mpcc_from_secondary) { |
| mpc->funcs->remove_mpcc_from_secondary(mpc, mpc_tree_params, mpcc_to_remove); |
| mpcc_removed = true; |
| } |
| |
| /* check if this MPCC is already being used for this plane (dpp) in the primary blending chain */ |
| mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id); |
| if (mpcc_to_remove != NULL) { |
| mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove); |
| mpcc_removed = true; |
| } |
| |
| /*Already reset*/ |
| if (mpcc_removed == false) |
| return; |
| |
| opp->mpcc_disconnect_pending[pipe_ctx->plane_res.mpcc_inst] = true; |
| |
| dc->optimized_required = true; |
| |
| if (hubp->funcs->hubp_disconnect) |
| hubp->funcs->hubp_disconnect(hubp); |
| |
| if (dc->debug.sanity_checks) |
| hws->funcs.verify_allow_pstate_change_high(dc); |
| } |
| |
| void dcn201_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) |
| { |
| struct hubp *hubp = pipe_ctx->plane_res.hubp; |
| struct mpcc_blnd_cfg blnd_cfg; |
| bool per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha && pipe_ctx->bottom_pipe; |
| int mpcc_id, dpp_id; |
| struct mpcc *new_mpcc; |
| struct mpcc *remove_mpcc = NULL; |
| struct mpc *mpc = dc->res_pool->mpc; |
| struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params); |
| |
| if (dc->debug.visual_confirm == VISUAL_CONFIRM_HDR) { |
| get_hdr_visual_confirm_color( |
| pipe_ctx, &blnd_cfg.black_color); |
| } else if (dc->debug.visual_confirm == VISUAL_CONFIRM_SURFACE) { |
| get_surface_visual_confirm_color( |
| pipe_ctx, &blnd_cfg.black_color); |
| } else { |
| color_space_to_black_color( |
| dc, pipe_ctx->stream->output_color_space, |
| &blnd_cfg.black_color); |
| } |
| |
| if (per_pixel_alpha) |
| blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA; |
| else |
| blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA; |
| |
| blnd_cfg.overlap_only = false; |
| |
| if (pipe_ctx->plane_state->global_alpha_value) |
| blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value; |
| else |
| blnd_cfg.global_alpha = 0xff; |
| |
| blnd_cfg.global_gain = 0xff; |
| blnd_cfg.background_color_bpc = 4; |
| blnd_cfg.bottom_gain_mode = 0; |
| blnd_cfg.top_gain = 0x1f000; |
| blnd_cfg.bottom_inside_gain = 0x1f000; |
| blnd_cfg.bottom_outside_gain = 0x1f000; |
| /*the input to MPCC is RGB*/ |
| blnd_cfg.black_color.color_b_cb = 0; |
| blnd_cfg.black_color.color_g_y = 0; |
| blnd_cfg.black_color.color_r_cr = 0; |
| |
| /* DCN1.0 has output CM before MPC which seems to screw with |
| * pre-multiplied alpha. This is a w/a hopefully unnecessary for DCN2. |
| */ |
| blnd_cfg.pre_multiplied_alpha = per_pixel_alpha; |
| |
| /* |
| * TODO: remove hack |
| * Note: currently there is a bug in init_hw such that |
| * on resume from hibernate, BIOS sets up MPCC0, and |
| * we do mpcc_remove but the mpcc cannot go to idle |
| * after remove. This cause us to pick mpcc1 here, |
| * which causes a pstate hang for yet unknown reason. |
| */ |
| dpp_id = hubp->inst; |
| mpcc_id = dpp_id; |
| |
| /* If there is no full update, don't need to touch MPC tree*/ |
| if (!pipe_ctx->plane_state->update_flags.bits.full_update) { |
| dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); |
| mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); |
| return; |
| } |
| |
| /* check if this plane is being used by an MPCC in the secondary blending chain */ |
| if (mpc->funcs->get_mpcc_for_dpp_from_secondary) |
| remove_mpcc = mpc->funcs->get_mpcc_for_dpp_from_secondary(mpc_tree_params, dpp_id); |
| |
| /* remove MPCC from secondary if being used */ |
| if (remove_mpcc != NULL && mpc->funcs->remove_mpcc_from_secondary) |
| mpc->funcs->remove_mpcc_from_secondary(mpc, mpc_tree_params, remove_mpcc); |
| |
| /* check if this MPCC is already being used for this plane (dpp) in the primary blending chain */ |
| remove_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id); |
| /* remove MPCC if being used */ |
| |
| if (remove_mpcc != NULL) |
| mpc->funcs->remove_mpcc(mpc, mpc_tree_params, remove_mpcc); |
| else |
| if (dc->debug.sanity_checks) |
| mpc->funcs->assert_mpcc_idle_before_connect( |
| dc->res_pool->mpc, mpcc_id); |
| |
| /* Call MPC to insert new plane */ |
| dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); |
| new_mpcc = mpc->funcs->insert_plane(dc->res_pool->mpc, |
| mpc_tree_params, |
| &blnd_cfg, |
| NULL, |
| NULL, |
| dpp_id, |
| mpcc_id); |
| |
| ASSERT(new_mpcc != NULL); |
| hubp->opp_id = pipe_ctx->stream_res.opp->inst; |
| hubp->mpcc_id = mpcc_id; |
| } |
| |
| void dcn201_pipe_control_lock( |
| struct dc *dc, |
| struct pipe_ctx *pipe, |
| bool lock) |
| { |
| struct dce_hwseq *hws = dc->hwseq; |
| /* use TG master update lock to lock everything on the TG |
| * therefore only top pipe need to lock |
| */ |
| if (pipe->top_pipe) |
| return; |
| |
| if (dc->debug.sanity_checks) |
| hws->funcs.verify_allow_pstate_change_high(dc); |
| |
| if (pipe->plane_state != NULL && pipe->plane_state->triplebuffer_flips) { |
| if (lock) |
| pipe->stream_res.tg->funcs->triplebuffer_lock(pipe->stream_res.tg); |
| else |
| pipe->stream_res.tg->funcs->triplebuffer_unlock(pipe->stream_res.tg); |
| } else { |
| if (lock) |
| pipe->stream_res.tg->funcs->lock(pipe->stream_res.tg); |
| else |
| pipe->stream_res.tg->funcs->unlock(pipe->stream_res.tg); |
| } |
| |
| if (dc->debug.sanity_checks) |
| hws->funcs.verify_allow_pstate_change_high(dc); |
| } |
| |
| void dcn201_set_cursor_attribute(struct pipe_ctx *pipe_ctx) |
| { |
| struct dc_cursor_attributes *attributes = &pipe_ctx->stream->cursor_attributes; |
| |
| gpu_addr_to_uma(pipe_ctx->stream->ctx->dc->hwseq, &attributes->address); |
| |
| pipe_ctx->plane_res.hubp->funcs->set_cursor_attributes( |
| pipe_ctx->plane_res.hubp, attributes); |
| pipe_ctx->plane_res.dpp->funcs->set_cursor_attributes( |
| pipe_ctx->plane_res.dpp, attributes); |
| } |
| |
| void dcn201_set_dmdata_attributes(struct pipe_ctx *pipe_ctx) |
| { |
| struct dc_dmdata_attributes attr = { 0 }; |
| struct hubp *hubp = pipe_ctx->plane_res.hubp; |
| |
| gpu_addr_to_uma(pipe_ctx->stream->ctx->dc->hwseq, |
| &pipe_ctx->stream->dmdata_address); |
| |
| attr.dmdata_mode = DMDATA_HW_MODE; |
| attr.dmdata_size = |
| dc_is_hdmi_signal(pipe_ctx->stream->signal) ? 32 : 36; |
| attr.address.quad_part = |
| pipe_ctx->stream->dmdata_address.quad_part; |
| attr.dmdata_dl_delta = 0; |
| attr.dmdata_qos_mode = 0; |
| attr.dmdata_qos_level = 0; |
| attr.dmdata_repeat = 1; /* always repeat */ |
| attr.dmdata_updated = 1; |
| attr.dmdata_sw_data = NULL; |
| |
| hubp->funcs->dmdata_set_attributes(hubp, &attr); |
| } |
| |
| void dcn201_unblank_stream(struct pipe_ctx *pipe_ctx, |
| struct dc_link_settings *link_settings) |
| { |
| struct encoder_unblank_param params = { { 0 } }; |
| struct dc_stream_state *stream = pipe_ctx->stream; |
| struct dc_link *link = stream->link; |
| struct dce_hwseq *hws = link->dc->hwseq; |
| |
| /* only 3 items below are used by unblank */ |
| params.timing = pipe_ctx->stream->timing; |
| |
| params.link_settings.link_rate = link_settings->link_rate; |
| |
| if (dc_is_dp_signal(pipe_ctx->stream->signal)) { |
| /*check whether it is half the rate*/ |
| if (pipe_ctx->stream_res.tg->funcs->is_two_pixels_per_container(&stream->timing)) |
| params.timing.pix_clk_100hz /= 2; |
| |
| pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); |
| } |
| |
| if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { |
| hws->funcs.edp_backlight_control(link, true); |
| } |
| } |