| /* |
| * Copyright 2012-16 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 <linux/slab.h> |
| |
| #include "dal_asic_id.h" |
| #include "dc_types.h" |
| #include "dccg.h" |
| #include "clk_mgr_internal.h" |
| |
| #include "dce100/dce_clk_mgr.h" |
| #include "dce110/dce110_clk_mgr.h" |
| #include "dce112/dce112_clk_mgr.h" |
| #include "dce120/dce120_clk_mgr.h" |
| #include "dce60/dce60_clk_mgr.h" |
| #include "dcn10/rv1_clk_mgr.h" |
| #include "dcn10/rv2_clk_mgr.h" |
| #include "dcn20/dcn20_clk_mgr.h" |
| #include "dcn21/rn_clk_mgr.h" |
| #include "dcn30/dcn30_clk_mgr.h" |
| #include "dcn301/vg_clk_mgr.h" |
| #include "dcn31/dcn31_clk_mgr.h" |
| |
| |
| int clk_mgr_helper_get_active_display_cnt( |
| struct dc *dc, |
| struct dc_state *context) |
| { |
| int i, display_count; |
| |
| display_count = 0; |
| for (i = 0; i < context->stream_count; i++) { |
| const struct dc_stream_state *stream = context->streams[i]; |
| |
| /* |
| * Only notify active stream or virtual stream. |
| * Need to notify virtual stream to work around |
| * headless case. HPD does not fire when system is in |
| * S0i2. |
| */ |
| if (!stream->dpms_off || stream->signal == SIGNAL_TYPE_VIRTUAL) |
| display_count++; |
| } |
| |
| return display_count; |
| } |
| |
| int clk_mgr_helper_get_active_plane_cnt( |
| struct dc *dc, |
| struct dc_state *context) |
| { |
| int i, total_plane_count; |
| |
| total_plane_count = 0; |
| for (i = 0; i < context->stream_count; i++) { |
| const struct dc_stream_status stream_status = context->stream_status[i]; |
| |
| /* |
| * Sum up plane_count for all streams ( active and virtual ). |
| */ |
| total_plane_count += stream_status.plane_count; |
| } |
| |
| return total_plane_count; |
| } |
| |
| void clk_mgr_exit_optimized_pwr_state(const struct dc *dc, struct clk_mgr *clk_mgr) |
| { |
| struct dc_link *edp_links[MAX_NUM_EDP]; |
| struct dc_link *edp_link = NULL; |
| int edp_num; |
| unsigned int panel_inst; |
| |
| get_edp_links(dc, edp_links, &edp_num); |
| if (dc->hwss.exit_optimized_pwr_state) |
| dc->hwss.exit_optimized_pwr_state(dc, dc->current_state); |
| |
| if (edp_num) { |
| for (panel_inst = 0; panel_inst < edp_num; panel_inst++) { |
| edp_link = edp_links[panel_inst]; |
| if (!edp_link->psr_settings.psr_feature_enabled) |
| continue; |
| clk_mgr->psr_allow_active_cache = edp_link->psr_settings.psr_allow_active; |
| dc_link_set_psr_allow_active(edp_link, false, false, false); |
| } |
| } |
| |
| } |
| |
| void clk_mgr_optimize_pwr_state(const struct dc *dc, struct clk_mgr *clk_mgr) |
| { |
| struct dc_link *edp_links[MAX_NUM_EDP]; |
| struct dc_link *edp_link = NULL; |
| int edp_num; |
| unsigned int panel_inst; |
| |
| get_edp_links(dc, edp_links, &edp_num); |
| if (edp_num) { |
| for (panel_inst = 0; panel_inst < edp_num; panel_inst++) { |
| edp_link = edp_links[panel_inst]; |
| if (!edp_link->psr_settings.psr_feature_enabled) |
| continue; |
| dc_link_set_psr_allow_active(edp_link, |
| clk_mgr->psr_allow_active_cache, false, false); |
| } |
| } |
| |
| if (dc->hwss.optimize_pwr_state) |
| dc->hwss.optimize_pwr_state(dc, dc->current_state); |
| |
| } |
| |
| struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *pp_smu, struct dccg *dccg) |
| { |
| struct hw_asic_id asic_id = ctx->asic_id; |
| |
| switch (asic_id.chip_family) { |
| #if defined(CONFIG_DRM_AMD_DC_SI) |
| case FAMILY_SI: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| dce60_clk_mgr_construct(ctx, clk_mgr); |
| dce_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| #endif |
| case FAMILY_CI: |
| case FAMILY_KV: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| dce_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| case FAMILY_CZ: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| dce110_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| case FAMILY_VI: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| if (ASIC_REV_IS_TONGA_P(asic_id.hw_internal_rev) || |
| ASIC_REV_IS_FIJI_P(asic_id.hw_internal_rev)) { |
| dce_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| if (ASIC_REV_IS_POLARIS10_P(asic_id.hw_internal_rev) || |
| ASIC_REV_IS_POLARIS11_M(asic_id.hw_internal_rev) || |
| ASIC_REV_IS_POLARIS12_V(asic_id.hw_internal_rev)) { |
| dce112_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| if (ASIC_REV_IS_VEGAM(asic_id.hw_internal_rev)) { |
| dce112_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| return &clk_mgr->base; |
| } |
| case FAMILY_AI: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| if (ASICREV_IS_VEGA20_P(asic_id.hw_internal_rev)) |
| dce121_clk_mgr_construct(ctx, clk_mgr); |
| else |
| dce120_clk_mgr_construct(ctx, clk_mgr); |
| return &clk_mgr->base; |
| } |
| #if defined(CONFIG_DRM_AMD_DC_DCN) |
| case FAMILY_RV: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| |
| if (ASICREV_IS_RENOIR(asic_id.hw_internal_rev)) { |
| rn_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| |
| if (ASICREV_IS_GREEN_SARDINE(asic_id.hw_internal_rev)) { |
| rn_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| if (ASICREV_IS_RAVEN2(asic_id.hw_internal_rev)) { |
| rv2_clk_mgr_construct(ctx, clk_mgr, pp_smu); |
| return &clk_mgr->base; |
| } |
| if (ASICREV_IS_RAVEN(asic_id.hw_internal_rev) || |
| ASICREV_IS_PICASSO(asic_id.hw_internal_rev)) { |
| rv1_clk_mgr_construct(ctx, clk_mgr, pp_smu); |
| return &clk_mgr->base; |
| } |
| return &clk_mgr->base; |
| } |
| case FAMILY_NV: { |
| struct clk_mgr_internal *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| if (ASICREV_IS_SIENNA_CICHLID_P(asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| if (ASICREV_IS_DIMGREY_CAVEFISH_P(asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| if (ASICREV_IS_BEIGE_GOBY_P(asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| dcn20_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base; |
| } |
| case FAMILY_VGH: |
| if (ASICREV_IS_VANGOGH(asic_id.hw_internal_rev)) { |
| struct clk_mgr_vgh *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| vg_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base.base; |
| } |
| break; |
| case FAMILY_YELLOW_CARP: { |
| struct clk_mgr_dcn31 *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); |
| |
| if (clk_mgr == NULL) { |
| BREAK_TO_DEBUGGER(); |
| return NULL; |
| } |
| if (ASICREV_IS_YELLOW_CARP(asic_id.hw_internal_rev)) { |
| /* TODO: to add DCN31 clk_mgr support, once CLK IP header files are available, |
| * for now use DCN3.0 clk mgr. |
| */ |
| dcn31_clk_mgr_construct(ctx, clk_mgr, pp_smu, dccg); |
| return &clk_mgr->base.base; |
| } |
| return &clk_mgr->base.base; |
| } |
| #endif |
| |
| default: |
| ASSERT(0); /* Unknown Asic */ |
| break; |
| } |
| |
| return NULL; |
| } |
| |
| void dc_destroy_clk_mgr(struct clk_mgr *clk_mgr_base) |
| { |
| struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); |
| |
| #ifdef CONFIG_DRM_AMD_DC_DCN |
| switch (clk_mgr_base->ctx->asic_id.chip_family) { |
| case FAMILY_NV: |
| if (ASICREV_IS_SIENNA_CICHLID_P(clk_mgr_base->ctx->asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_destroy(clk_mgr); |
| } |
| if (ASICREV_IS_DIMGREY_CAVEFISH_P(clk_mgr_base->ctx->asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_destroy(clk_mgr); |
| } |
| if (ASICREV_IS_BEIGE_GOBY_P(clk_mgr_base->ctx->asic_id.hw_internal_rev)) { |
| dcn3_clk_mgr_destroy(clk_mgr); |
| } |
| break; |
| |
| case FAMILY_VGH: |
| if (ASICREV_IS_VANGOGH(clk_mgr_base->ctx->asic_id.hw_internal_rev)) |
| vg_clk_mgr_destroy(clk_mgr); |
| break; |
| |
| case FAMILY_YELLOW_CARP: |
| if (ASICREV_IS_YELLOW_CARP(clk_mgr_base->ctx->asic_id.hw_internal_rev)) |
| dcn31_clk_mgr_destroy(clk_mgr); |
| break; |
| |
| default: |
| break; |
| } |
| #endif |
| |
| kfree(clk_mgr); |
| } |
| |