From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5F4CAFA0C43 for ; Wed, 15 Apr 2026 07:43:39 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id F090A10E699; Wed, 15 Apr 2026 07:43:38 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (1024-bit key; unprotected) header.d=amd.com header.i=@amd.com header.b="eBxfnwWt"; dkim-atps=neutral Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010021.outbound.protection.outlook.com [52.101.56.21]) by gabe.freedesktop.org (Postfix) with ESMTPS id 18AD010E699 for ; Wed, 15 Apr 2026 07:43:38 +0000 (UTC) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=iHWqEZmu43/4UacTkTwH4wPOejVDZg+ep0DS9ukmMHyTk8xeDGBhfneBjBMne/yG4JAdkh8Ja/RcgLQo7CmVnkHgg15/PfdXW8FV5VlRMK2XhyIGmis7sImkxoZcG97Erg32LrrVVLwWR31f3GcOoMx+NpuVnlC0xPmexkWlvmX3Xbxm0NTzuorQEz0vR56zVkQ5o6DPqhZy6GYJrVzYlsx2hpvEVvN8t/rGaQoy7fEmdo+aktgj0xXl0TNOHnj/z+PkZ8uOVttKHgE/DVr0ApuTjDhiN5Uhhi6wOR4r0HA32Ng/x7wLDZKvm8rjtbCKFwtFcpU1/kNr8faWMOhw2Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=hUcoj0aud5ZFYWF/dozP5BrbM1iZTSdiEH2XCVEnkf4=; b=m9xBLr1Wd3PEhnSuN78t4m2YgUpzGseDi90BvfimR5FmNM+PKjyuyeQ2eHJ/wcy+aKuGAB3HVIaK/res8Y2v8tdXUy/EwPc42PPERwRQSllczWG0NkXlkI3CpZsd8r4OZZgTtB6nF8Mp/WGizkBd15CwVe+mGJNmmu4kf4Hew5r8GvGdsyC/N7s/WE32cHm+hTQS95hk0nshVpj3QUsosZ8pYA/kDMYFPwHwEhW31HdBBhxIwV6/We5DCEc5RV0YRvTlUEqXbqQZYk9xMjZdN5o7AV2f/yej97UoVZnsugcim5KVhFie9q3dAw5DbxtbjnQAsAMvgj7ugoG+BCIdNQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 165.204.84.17) smtp.rcpttodomain=lists.freedesktop.org smtp.mailfrom=amd.com; dmarc=pass (p=quarantine sp=quarantine pct=100) action=none header.from=amd.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=hUcoj0aud5ZFYWF/dozP5BrbM1iZTSdiEH2XCVEnkf4=; b=eBxfnwWtlljN1W3NWWCw4FEVayfRwYvSt7G0tzhzofzjmQObokhNaLbRj1XjoB0deBvjwwmK2W+0CTr0gydC3fLVH6yPTx+PRaI5Xwxnpf69MLBmEkuN8igZm8ky3+CNQZkrLi1MCcPSXaNOUh8OHLt6LrZCz68o0vILWQ8/NYw= Received: from BL0PR1501CA0034.namprd15.prod.outlook.com (2603:10b6:207:17::47) by LV3PR12MB9186.namprd12.prod.outlook.com (2603:10b6:408:197::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9818.20; Wed, 15 Apr 2026 07:43:30 +0000 Received: from MN1PEPF0000F0DF.namprd04.prod.outlook.com (2603:10b6:207:17:cafe::c3) by BL0PR1501CA0034.outlook.office365.com (2603:10b6:207:17::47) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9769.48 via Frontend Transport; Wed, 15 Apr 2026 07:43:30 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 165.204.84.17) smtp.mailfrom=amd.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=amd.com; Received-SPF: Pass (protection.outlook.com: domain of amd.com designates 165.204.84.17 as permitted sender) receiver=protection.outlook.com; client-ip=165.204.84.17; helo=satlexmb07.amd.com; pr=C Received: from satlexmb07.amd.com (165.204.84.17) by MN1PEPF0000F0DF.mail.protection.outlook.com (10.167.242.37) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9769.17 via Frontend Transport; Wed, 15 Apr 2026 07:43:30 +0000 Received: from satlexmb08.amd.com (10.181.42.217) by satlexmb07.amd.com (10.181.42.216) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.17; Wed, 15 Apr 2026 02:43:29 -0500 Received: from chenyu-station.amd.com (10.180.168.240) by satlexmb08.amd.com (10.181.42.217) with Microsoft SMTP Server id 15.2.2562.17 via Frontend Transport; Wed, 15 Apr 2026 02:43:21 -0500 From: Chenyu Chen To: CC: Harry Wentland , Leo Li , Aurabindo Pillai , Roman Li , Wayne Lin , Tom Chung , "Fangzhi Zuo" , Dan Wheeler , Ray Wu , Ivan Lipski , Alex Hung , Chuanyu Tseng , Ray Wu , Chenyu Chen Subject: [PATCH 08/19] drm/amd/display: Introduce power module on Linux Date: Wed, 15 Apr 2026 15:39:47 +0800 Message-ID: <20260415074223.34848-9-chen-yu.chen@amd.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260415074223.34848-1-chen-yu.chen@amd.com> References: <20260415074223.34848-1-chen-yu.chen@amd.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: MN1PEPF0000F0DF:EE_|LV3PR12MB9186:EE_ X-MS-Office365-Filtering-Correlation-Id: 3ff428af-31c6-462d-92cf-08de9ac2b02a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|36860700016|82310400026|376014|1800799024|18096099003|22082099003|18002099003|56012099003; X-Microsoft-Antispam-Message-Info: whQ12KN0A/wO/8FX7oU5w04SCkhoUVY7JU76+7OfsC6aB2nHzz0T1vUewhNlZYT5Rv1v2jNO0eV2nGT3KO+Io4eSd13dsd/57AHHtjpkysVqmt4WBQXHYy1NGcJPAemdLTpgwwmqV1w6VQ2zevseYCcjm9ARQeXyFN6Bxchwe8+SNlbUv1Lgp6xHiIyit/TdDbybFsVdy6M7e9Kn36IzPMRL0O4mTRP3Q4IeKHEetvVtYAQVHA9TgBBousbZmm5mCSoMY9+bG1tDIGJvKaFLbWgDPJOR5SqMipvRRJnrnSEhiH8ZHpMgbxKwlUvWeErH0lsdexG4T/gd5+rQiFsBGHkvOfOWtlpzGnUQbbxWuo/KpWyxQK+YkIRkk91LXIZ60Hnb3aYtaHKWSxsf2FtScmwBKrQoEHy/t6W2YHe7ofOgMP6LxFA1YSDx7kUiJQ92ss3Tkyrxy1odH3HK6GghgIrVkqv/fnjK6T+G+nk7DZbp5lYm+GHF4RQaAFY8Lwtz2d2Hb6N5les1t/Nb7s0FKFmb8RbQuSjeHZZvHZiP594fb2MHvbzVhJCcO12ir6auFXcGNX3UJeTygD2nQ7YydXx8km9/wQ39i5GTvU5P1xguK0F7a5jfhr3Q8Vb/A9x6dsU5QFXkN0unscilOxSQ8ZbYuajxbjWn3y3PAgThJAXp6KkjPdg5uqTLrAJ7VPu/qd5wrIS6sPSSvj6rCTVXTfrGVsO9ClMTr6DesVScJF6XvLBFTPd1+2GJygbzfJr3McTbjE4s4kggUzev/hCqoQ== X-Forefront-Antispam-Report: CIP:165.204.84.17; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:satlexmb07.amd.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(36860700016)(82310400026)(376014)(1800799024)(18096099003)(22082099003)(18002099003)(56012099003); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Sov+DObYZwTPvvEtPX7RAVmPfACYynO6/WQDFsLTnU+n7BQKHoFQGoUJ+rEdshZEY3zwJYpPPmvEGK1nBTlQN9SmDiDfeDYlsepCO4QW8uyKAvS3kRslrTRuBRwAXDotmwelXGrRmbYGdbG+TDHJodoz4qfMSBxZbj+w2JCyc31ylN+VHuFtQhTAqkhlQwqgEQoUMWtspkDfktnB2zBymVRterYHcNPWrS9hTrJp6RVStnLMeLGtWFCMuCLilKUpePJ+UwvoVTJ9DJTml0W0Tzxghbmsucs4JPv6VVAkpvIHL+B/80Q3RtXVrJ0t711Qv7RQNx0MGhBQgBuLGREuiZND0PwVJo8+QiLvKmDOT9q7YkP7Bx5i0puF6j/hGWoizdLoKYD55ReJC8OIpJw50NzsrJlb4dIxLwTeSLsjwZtRdUQ0EuEC00/4hpYOlLcZ X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Apr 2026 07:43:30.2751 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 3ff428af-31c6-462d-92cf-08de9ac2b02a X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[satlexmb07.amd.com] X-MS-Exchange-CrossTenant-AuthSource: MN1PEPF0000F0DF.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: LV3PR12MB9186 X-BeenThere: amd-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Discussion list for AMD gfx List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: amd-gfx-bounces@lists.freedesktop.org Sender: "amd-gfx" From: Ray Wu [Why] Other OS supported by DC uses the power module to manage panel power features such as backlight and self-refresh. It contains enhancements on top what amdgpu_dm is doing today that can benefit power. [How] Introduce the power module. It's currently not being used anywhere, a future change will incorporate it into amdgpu_dm. Reviewed-by: Leo Li Signed-off-by: Ray Wu Signed-off-by: Leo Li Signed-off-by: Chenyu Chen --- .../display/amdgpu_dm/amdgpu_dm_services.c | 11 + .../gpu/drm/amd/display/dc/core/dc_stream.c | 6 + drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 + .../drm/amd/display/modules/inc/mod_power.h | 415 +++ .../drm/amd/display/modules/power/Makefile | 2 +- .../gpu/drm/amd/display/modules/power/power.c | 3030 +++++++++++++++++ 6 files changed, 3466 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/display/modules/inc/mod_power.h create mode 100644 drivers/gpu/drm/amd/display/modules/power/power.c diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c index 8550d5e8b753..0ef7435ffda9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -62,3 +62,14 @@ void dm_trace_smu_exit(bool success, uint32_t response, struct dc_context *ctx) } /**** power component interfaces ****/ + +bool dm_query_extended_brightness_caps(struct dc_context *ctx, + enum dm_acpi_display_type display, + struct dm_acpi_atif_backlight_caps *pCaps) +{ + /* + * TODO: Implement query for extended backlight caps. + * Some plumbing required, see amdgpu_atif_query_backlight_caps() + */ + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index cca3dece08d3..9c1d721011ca 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -259,6 +259,12 @@ const struct dc_stream_status *dc_stream_get_status_const( return dc_state_get_stream_status(dc->current_state, stream); } +struct dc_link *dc_stream_get_link( + const struct dc_stream_state *stream) +{ + return stream->link; +} + void program_cursor_attributes( struct dc *dc, struct dc_stream_state *stream) diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 88f70a9b64b1..6a8c1390b85f 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -494,6 +494,9 @@ struct surface_update_descriptor dc_check_update_surfaces_for_stream( int surface_count, struct dc_stream_update *stream_update); +struct dc_link *dc_stream_get_link( + const struct dc_stream_state *dc_stream); + /** * Create a new default stream for the requested sink */ diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_power.h b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h new file mode 100644 index 000000000000..89037f7b7961 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_power.h @@ -0,0 +1,415 @@ +/* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. */ + +#ifndef MODULES_INC_MOD_POWER_H_ +#define MODULES_INC_MOD_POWER_H_ + +#include "dm_services.h" + +struct mod_power_init_params { + + bool disable_fractional_pwm; + + /* Use nits based brightness instead of brightness percentage + */ + bool use_nits_based_brightness; + unsigned int panel_min_millinits; + unsigned int panel_max_millinits; + + unsigned int min_backlight_pwm; + unsigned int max_backlight_pwm; + + unsigned int min_abm_backlight; + unsigned int num_backlight_levels; + bool backlight_ramping_override; + unsigned int backlight_ramping_reduction; + unsigned int backlight_ramping_start; + bool def_varibright_enable; + unsigned int def_varibright_level; + unsigned int varibright_level; + unsigned int abm_config_setting; + + bool allow_psr_smu_optimizations; + + bool allow_psr_multi_disp_optimizations; + + bool use_custom_backlight_caps; + unsigned int custom_backlight_caps_config_no; + bool use_linear_backlight_curve; +}; + +struct mod_power { + int dummy; +}; + +/* VariBright settings structure */ +struct varibright_info { + unsigned int level; + bool enable; + bool activate; +}; + +struct mod_power_psr_context { + /* ddc line */ + unsigned int channel; + /* Transmitter id */ + unsigned int transmitter_id; + /* Engine Id is used for Dig Be source select */ + unsigned int engine_id; + /* Controller Id used for Dig Fe source select */ + unsigned int controller_id; + /* Pcie or Uniphy */ + unsigned int phy_type; + /* Physical PHY Id used by SMU interpretation */ + unsigned int smu_phy_id; + /* Vertical total pixels from crtc timing. + * This is used for static screen detection. + * ie. If we want to detect half a frame, + * we use this to determine the hyst lines. + */ + unsigned int crtc_timing_vertical_total; + /* PSR supported from panel capabilities and + * current display configuration + */ + bool psr_supported_display_config; + /* Whether fast link training is supported by the panel */ + bool psr_exit_link_training_required; + /* If RFB setup time is greater than the total VBLANK time, + * it is not possible for the sink to capture the video frame + * in the same frame the SDP is sent. In this case, + * the frame capture indication bit should be set and an extra + * static frame should be transmitted to the sink. + */ + bool psr_frame_capture_indication_req; + /* Set the last possible line SDP may be transmitted without violating + * the RFB setup time or entering the active video frame. + */ + unsigned int sdp_transmit_line_num_deadline; + /* The VSync rate in Hz used to calculate the + * step size for smooth brightness feature + */ + unsigned int vsync_rate_hz; + unsigned int skip_psr_wait_for_pll_lock; + unsigned int number_of_controllers; + /* Unused, for future use. To indicate that first changed frame from + * state3 shouldn't result in psr_inactive, but rather to perform + * an automatic single frame rfb_update. + */ + bool rfb_update_auto_en; + /* Number of frame before entering static screen */ + unsigned int timehyst_frames; + /* Partial frames before entering static screen */ + unsigned int hyst_lines; + /* # of repeated AUX transaction attempts to make before + * indicating failure to the driver + */ + unsigned int aux_repeats; + /* Controls hw blocks to power down during PSR active state */ + unsigned int psr_level; + /* Controls additional delay after remote frame capture before + * continuing powerd own + */ + unsigned int frame_delay; + bool allow_smu_optimizations; + bool allow_multi_disp_optimizations; + unsigned int line_time_in_us; + /* Panel self refresh 2 selective update granularity required */ + bool su_granularity_required; + /* psr2 selective update y granularity capability */ + uint8_t su_y_granularity; + uint8_t rate_control_caps; + bool os_request_force_ffu; +}; + +enum psr_event { + psr_event_invalid = 0x0, + psr_event_vsync = 0x1, + psr_event_full_screen = 0x2, + psr_event_defer_enable = 0x4, + psr_event_hw_programming = 0x8, + psr_event_test_harness_enable_psr = 0x10, + psr_event_test_harness_disable_psr = 0x20, + psr_event_mpo_video_selective_update = 0x40, + psr_event_edp_panel_off_disable_psr = 0x80, + psr_event_dynamic_display_switch = 0x100, + psr_event_big_screen_video = 0x200, + psr_event_dds_defer_stream_enable = 0x800, + psr_event_dynamic_link_rate_control = 0x1000, + psr_event_vrr_transition = 0x2000, + psr_event_pause = 0x4000, + psr_event_immediate_flip = 0x8000, + psr_event_os_request_disable = 0x10000, + psr_event_os_request_force_ffu = 0x20000, + psr_event_os_override_hold = 0x40000, + psr_event_crc_window_active = 0x80000, +}; + +enum replay_event { + replay_event_invalid = 0x0, + replay_event_vsync = 0x1, + replay_event_full_screen = 0x2, + replay_event_mpo_video_selective_update = 0x4, + replay_event_big_screen_video = 0x8, + replay_event_hw_programming = 0x10, + replay_event_edp_panel_off_disable_psr = 0x20, + replay_event_general_ui = 0x40, + replay_event_vrr = 0x80, + replay_event_prepare_vtotal = 0x100, + replay_event_test_harness_enable_replay = 0x200, + replay_event_test_harness_disable_replay = 0x400, + replay_event_test_harness_ultra_sleep = 0x800, + replay_event_immediate_flip = 0x1000, + replay_event_vrr_transition = 0x2000, + replay_event_pause = 0x4000, + replay_event_disable_replay_while_DPMS = 0x8000, + replay_event_test_harness_mode = 0x10000, + replay_event_cursor_updating = 0x20000, + replay_event_sleep_resume = 0x40000, + replay_event_disable_in_AC = 0x80000, + replay_event_disable_replay_while_detect_display = 0x100000, + replay_event_disable_replay_while_switching_mux = 0x400000, + replay_event_infopacket = 0x800000, + replay_event_os_request_disable = 0x1000000, + replay_event_os_request_force_ffu = 0x2000000, + replay_event_os_override_hold = 0x4000000, + replay_event_crc_window_active = 0x8000000, +}; + +enum replay_enable_option { + pr_enable_option_static_screen = 0x1, + pr_enable_option_mpo_video = 0x2, + pr_enable_option_full_screen_video = 0x4, + pr_enable_option_general_ui = 0x8, + pr_enable_option_full_screen = 0x10, + pr_enable_option_static_screen_coasting = 0x10000, + pr_enable_option_mpo_video_coasting = 0x20000, + pr_enable_option_full_screen_video_coasting = 0x40000, + pr_enable_option_full_screen_coasting = 0x100000, +}; + +struct mod_power *mod_power_create(struct dc *dc, + struct mod_power_init_params *init_params, + unsigned int edp_num); + +void mod_power_destroy(struct mod_power *mod_power); + +bool mod_power_hw_init(struct mod_power *mod_power); + +bool mod_power_add_stream(struct mod_power *mod_power, + struct dc_stream_state *stream, struct psr_caps *caps); + +bool mod_power_remove_stream(struct mod_power *mod_power, + const struct dc_stream_state *stream); + +bool mod_power_replace_stream(struct mod_power *mod_power, + const struct dc_stream_state *current_stream, + struct dc_stream_state *new_stream, + struct psr_caps *new_caps); + +bool mod_power_set_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *streams, + unsigned int backlight_millinit, + unsigned int transition_time_millisec, + bool skip_aux, + bool is_hdr); + +bool mod_power_set_backlight_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int transition_time_millisec, + bool is_hdr); + +void mod_power_update_backlight(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent); + +void mod_power_update_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit); + +bool mod_power_get_backlight_pwm(struct mod_power *mod_power, + unsigned int *backlight_pwm, + unsigned int inst); + +bool mod_power_get_backlight_nits(struct mod_power *mod_power, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_backlight_percent(struct mod_power *mod_power, + unsigned int *backlight_millipercent, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm_nits( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm_percent( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst); + +bool mod_power_get_hw_target_backlight_pwm( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16); + +bool mod_power_get_hw_backlight_pwm( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight); + +bool mod_power_get_hw_backlight_pwm_nits( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst); + +bool mod_power_get_hw_backlight_aux_nits( + struct mod_power *mod_power, + struct dc_stream_state **streams, int num_streams, + unsigned int *backlight_millinit_avg, + unsigned int *backlight_millinit_peak); + +bool mod_power_get_hw_backlight_pwm_percent( + struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst); + +void mod_power_initialize_backlight_caps + (struct mod_power *mod_power); + +bool mod_power_get_panel_backlight_boundaries + (struct mod_power *mod_power, + unsigned int *out_min_backlight, + unsigned int *out_max_backlight, + unsigned int *out_ac_backlight_percent, + unsigned int *out_dc_backlight_percent, + unsigned int inst); + +bool mod_power_set_smooth_brightness(struct mod_power *mod_power, + bool enable_brightness, + unsigned int inst); + +bool mod_power_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool is_hdr); + +bool mod_power_get_varibright_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_hw_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_default_level(struct mod_power *mod_power, + unsigned int *varibright_level); + +bool mod_power_get_varibright_enable(struct mod_power *mod_power, + bool *varibright_enable); + +bool mod_power_varibright_activate(struct mod_power *mod_power, + bool activate, struct dc_stream_update *stream_update); + +bool mod_power_varibright_feature_enable(struct mod_power *mod_power, + bool enable, struct dc_stream_update *stream_update); + + +bool mod_power_varibright_set_level(struct mod_power *mod_power, + unsigned int level, struct dc_stream_update *stream_update); + +bool mod_power_varibright_set_hw_level(struct mod_power *mod_power, + unsigned int level, struct dc_stream_update *stream_update); + +bool mod_power_is_abm_active(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int inst); + + +bool mod_power_set_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum psr_event event, bool wait); + +bool mod_power_get_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_psr_events); + +bool mod_power_get_psr_state(struct mod_power *mod_power, + const struct dc_stream_state *stream, + enum dc_psr_state *state); + +bool mod_power_get_psr_enabled_status(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool *psr_enabled); + +bool mod_power_set_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum replay_event event, bool wait_for_disable); + +bool mod_power_get_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_replay_events); + +bool mod_power_get_replay_active_status(const struct dc_stream_state *stream, + bool *replay_active); + +bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint32_t coasting_vtotal, uint16_t frame_skip_number); + +void mod_power_replay_residency(const struct dc_stream_state *stream, + unsigned int *residency, const bool is_start, const bool is_alpm); + +bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, + bool is_ultra_sleep_mode, uint16_t frame_skip_number); + +void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power, + const struct dc_stream_state *stream); + +void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled); + +void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled); +void mod_power_set_low_rr_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool low_rr_supported); + +void mod_power_set_video_conferencing_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool video_conferencing_activate); + +void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool live_capture_with_cvt_activate); + +void mod_power_set_replay_continuously_resync(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool enable); + +void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint32_t coasting_vtotal); + + + +void mod_power_psr_residency(struct mod_power *mod_power, + const struct dc_stream_state *stream, + unsigned int *residency, + const uint8_t mode); +bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int *active_psr_events); +bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint16_t psr_vtotal_idle, + uint16_t psr_vtotal_su); + + + +bool mod_power_backlight_percent_to_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int *backlight_millinit); +bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int *backlight_millipercent); + +#endif /* MODULES_INC_MOD_POWER_H_ */ diff --git a/drivers/gpu/drm/amd/display/modules/power/Makefile b/drivers/gpu/drm/amd/display/modules/power/Makefile index 9d1b22d35ece..b27a1ff3d86b 100644 --- a/drivers/gpu/drm/amd/display/modules/power/Makefile +++ b/drivers/gpu/drm/amd/display/modules/power/Makefile @@ -23,7 +23,7 @@ # Makefile for the 'power' sub-module of DAL. # -MOD_POWER = power_helpers.o +MOD_POWER = power_helpers.o power.o AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER)) #$(info ************ DAL POWER MODULE MAKEFILE ************) diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c new file mode 100644 index 000000000000..6c73fecf57d5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/power/power.c @@ -0,0 +1,3030 @@ +/* + * 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 "dc.h" +#include "mod_power.h" +#include "core_types.h" +#include "dmcu.h" +#include "abm.h" +#include "power_helpers.h" +#include "dce/dmub_psr.h" +#include "dal_asic_id.h" +#include "link_service.h" +#include + +#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */ +#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */ + +#define MOD_POWER_MAX_CONCURRENT_STREAMS 32 +#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500 +#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000 + + +struct backlight_state { + /* HW uses u16.16 format for backlight PWM */ + unsigned int backlight_pwm; + /* DM may call power module to set backlight + * targeting percent brightness + */ + unsigned int backlight_millipercent; + /* DM may call power module to set backlight based on an explicit + * nits value. + */ + unsigned int backlight_millinit; + unsigned int frame_ramp; + bool smooth_brightness_enabled; + bool isHDR; +}; +struct power_entity { + struct dc_stream_state *stream; + struct psr_caps *caps; + struct mod_power_psr_context *psr_context; + + /*PSR cached properties*/ + bool psr_enabled; + unsigned int psr_events; + unsigned int psr_power_opt; + unsigned int replay_events; +}; + +struct backlight_properties { + bool use_nits_based_brightness; + bool disable_fractional_pwm; + + unsigned int min_abm_backlight; + unsigned int num_backlight_levels; + + bool backlight_ramping_override; + unsigned int backlight_ramping_reduction; + unsigned int backlight_ramping_start; + + /* Backlight cached properties */ + unsigned int ac_backlight_percent; + unsigned int dc_backlight_percent; + + /* backlight LUT stored in HW u16.16 format*/ + unsigned int *backlight_lut; + unsigned int min_backlight_pwm; + unsigned int max_backlight_pwm; + unsigned int backlight_range; + + /* Describes the panel's min and max luminance in millinits measured + * on full white screen, in min and max backlight settings. + */ + unsigned int min_brightness_millinits; + unsigned int max_brightness_millinits; + unsigned int nits_range; + + bool backlight_caps_valid; + bool use_custom_backlight_caps; + unsigned int custom_backlight_caps_config_no; + bool use_linear_backlight_curve; +}; + +struct dmcu_varibright_cached_properties { + unsigned int varibright_config_setting; + unsigned int varibright_level; + unsigned int varibright_hw_level; + unsigned int def_varibright_level; + bool varibright_user_enable; + bool varibright_active; +}; + +struct core_power { + struct mod_power public; + struct dc *dc; + struct power_entity *map; + struct dmcu_varibright_cached_properties varibright_prop; + struct backlight_properties bl_prop[MAX_NUM_EDP]; + struct backlight_state bl_state[MAX_NUM_EDP]; + unsigned int edp_num; + + bool psr_smu_optimizations_support; + bool multi_disp_optimizations_support; + + int num_entities; +}; + +union dmcu_abm_set_bl_params { + struct { + unsigned int gradual_change : 1; /* [0:0] */ + unsigned int reserved : 15; /* [15:1] */ + unsigned int frame_ramp : 16; /* [31:16] */ + } bits; + unsigned int u32All; +}; + +/* If system or panel does not report some sort of brightness percent to nits + * mapping, we will use following default values so backlight control using + * nits based interfaces will still work, but might not describe panel + * correctly. In this case percentage based backlight control should ideally + * be used. + * Min = 5 nits + * Max = 300 nits + */ + +static const unsigned int pwr_default_min_brightness_millinits = 1000; +static const unsigned int pwr_default_sdr_brightness_millinits = 270000; + +static const unsigned int default_ac_backlight_percent = 100; +static const unsigned int default_dc_backlight_percent = 70; + +#define MOD_POWER_TO_CORE(mod_power)\ + container_of(mod_power, struct core_power, public) + +static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz) +{ + /* Calculate number of static frames before generating interrupt to + * enter PSR. + */ + unsigned int frame_time_microsec = 1000000 / vsync_rate_hz; + + // Init fail safe of 2 frames static + unsigned int num_frames_static = 2; + + /* Round up + * Calculate number of frames such that at least 30 ms of time has + * passed. + */ + if (vsync_rate_hz != 0) + num_frames_static = (30000 / frame_time_microsec) + 1; + + return num_frames_static; +} + +/* Given a specific dc_stream* this function finds its equivalent + * on the core_freesync->map and returns the corresponding index + */ +static unsigned int map_index_from_stream(struct core_power *core_power, + const struct dc_stream_state *stream) +{ + unsigned int index = 0; + + for (index = 0; index < core_power->num_entities; index++) { + if (core_power->map[index].stream == stream) + return index; + } + /* Could not find stream requested, this is not trivial, fix when hit*/ + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "map index from stream: ERROR: core_power=%p stream=%p", + core_power, + stream); + ASSERT(false); + /* We come here only when we can't map stream index. + * In good cases, this would happen when we attempt to change + * brightness before stream creation, in which case we create a + * dummy stream with index 0. + * With external monitor connected, the index passed from this return + * is 1. Passing anything greater than 0 from here would always point + * to bad memory. + */ + return 0; +} + +static uint16_t backlight_8_to_16(unsigned int backlight_8bit) +{ + return (uint16_t)(backlight_8bit * 0x101); +} + + +static unsigned int backlight_millipercent_to_millinit( + struct core_power *core_power, unsigned int millipercent, unsigned int inst) +{ + unsigned int millinit = 0; + unsigned long long numerator = 0; + + if (core_power == NULL) + return 0; + + numerator = ((unsigned long long)millipercent) * + core_power->bl_prop[inst].nits_range; + millinit = ((unsigned int)div_u64(numerator, 100000)) + + core_power->bl_prop[inst].min_brightness_millinits; + + return millinit; +} + +static unsigned int backlight_millinit_to_millipercent( + struct core_power *core_power, unsigned int millinit, unsigned int inst) +{ + unsigned int millipercent = 0; + unsigned long long numerator = 0; + + if (core_power == NULL) + return 0; + + if (millinit <= core_power->bl_prop[inst].min_brightness_millinits) + return 0; + + if (millinit >= core_power->bl_prop[inst].max_brightness_millinits) + return (100 * 1000); + + numerator = (((unsigned long long)millinit) - + core_power->bl_prop[inst].min_brightness_millinits) * 100000; + millipercent = ((unsigned int)div_u64(numerator, + core_power->bl_prop[inst].nits_range)); + + return millipercent; +} + +static unsigned int backlight_pwm_to_millipercent( + struct core_power *core_power, unsigned int pwm, unsigned int inst) +{ + unsigned int millipercent = 0; + unsigned int max_index = 0; + + if (core_power == NULL) + return 0; + + if (!core_power->bl_prop[inst].backlight_caps_valid) + return 0; + + /* Doesn't really make sense to have one single backlight level + * possible... + */ + if (core_power->bl_prop[inst].num_backlight_levels < 2) + return 0; + + max_index = core_power->bl_prop[inst].num_backlight_levels - 1; + + if (pwm <= core_power->bl_prop[inst].backlight_lut[0]) + return 0; + + if (pwm > core_power->bl_prop[inst].backlight_lut[max_index]) + return (100 * 1000); + + /* We need to do a binary search over the array for where the pwm level + * is in the lut. Based on the index we can determine percentage. + */ + unsigned int min = 0; + unsigned int max = max_index; + unsigned int mid = 0; + + while (max >= min) { + mid = (min + max) / 2; /* floor of half range */ + + if (core_power->bl_prop[inst].backlight_lut[mid] < pwm) + min = mid + 1; + else if (core_power->bl_prop[inst].backlight_lut[mid] > pwm) + max = mid - 1; + else + break; + } + + /* In this case, exact match is not found. Check if mid/min/max + * value is actually closer. + */ + if (max < min) { + unsigned int min_delta; + unsigned int mid_delta; + unsigned int max_delta; + + min_delta = (core_power->bl_prop[inst].backlight_lut[min] > pwm) ? + core_power->bl_prop[inst].backlight_lut[min] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[min]; + + mid_delta = (core_power->bl_prop[inst].backlight_lut[mid] > pwm) ? + core_power->bl_prop[inst].backlight_lut[mid] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[mid]; + + max_delta = (core_power->bl_prop[inst].backlight_lut[max] > pwm) ? + core_power->bl_prop[inst].backlight_lut[max] - pwm : + pwm - core_power->bl_prop[inst].backlight_lut[max]; + + if ((min_delta < mid_delta) && (min_delta < max_delta)) + mid = min; + + if ((max_delta < mid_delta) && (max_delta < min_delta)) + mid = max; + } + + /* No interpolation, just take closest index */ + millipercent = 1000 * 100 * mid / max_index; + + return millipercent; +} + +static unsigned int backlight_pwm_to_millinit( + struct core_power *core_power, unsigned int pwm, unsigned int inst) +{ + unsigned int millinit = 0; + + if (core_power == NULL) + return 0; + + if (pwm <= core_power->bl_prop[inst].min_backlight_pwm) + return core_power->bl_prop[inst].min_brightness_millinits; + + if (pwm >= core_power->bl_prop[inst].max_backlight_pwm) + return core_power->bl_prop[inst].max_brightness_millinits; + + millinit = ((unsigned int)div_u64(((unsigned long long)pwm - + core_power->bl_prop[inst].min_backlight_pwm) * + core_power->bl_prop[inst].nits_range, + core_power->bl_prop[inst].backlight_range)); + + millinit += core_power->bl_prop[inst].min_brightness_millinits; + + if (millinit > core_power->bl_prop[inst].max_brightness_millinits) + millinit = core_power->bl_prop[inst].max_brightness_millinits; + + return millinit; +} + +static unsigned int backlight_millipercent_to_pwm( + struct core_power *core_power, unsigned int millipercent, unsigned int inst) +{ + unsigned int pwm = (unsigned int)-1; + unsigned int index = 0; + + if (core_power == NULL) + return 0; + + // Bypass the brightness mapping LUT + if (core_power->bl_prop->use_linear_backlight_curve) { + pwm = core_power->bl_prop[inst].min_backlight_pwm + + (unsigned int) div_u64((unsigned long long) millipercent * + core_power->bl_prop[inst].backlight_range, + 100000); + + if (pwm > core_power->bl_prop[inst].max_backlight_pwm) + pwm = core_power->bl_prop[inst].max_backlight_pwm; + + return pwm; + } + + if (millipercent >= (100 * 1000)) + return core_power->bl_prop[inst].backlight_lut[core_power->bl_prop[inst].num_backlight_levels - 1]; + + /* This will give the floor index. */ + index = ((core_power->bl_prop[inst].num_backlight_levels - 1) * + millipercent) / 100000; + /* Null check otherwise eDP doesn't lightup when connected to DP1 */ + if (core_power->bl_prop[inst].backlight_lut == NULL) + return pwm; + + pwm = core_power->bl_prop[inst].backlight_lut[index]; + + return pwm; +} + +static unsigned int backlight_millinit_to_pwm( + struct core_power *core_power, unsigned int millinit, unsigned int inst) +{ + unsigned int pwm = 0; + + if (core_power == NULL) + return 0; + + /* For nits based brightness, the signal will be a value + * between the minimum and maximum value. + */ + if (millinit >= core_power->bl_prop[inst].max_brightness_millinits) + return core_power->bl_prop[inst].max_backlight_pwm; + else if (millinit <= core_power->bl_prop[inst].min_brightness_millinits) + return core_power->bl_prop[inst].min_backlight_pwm; + + pwm = ((unsigned int)div_u64(((unsigned long long)millinit - + core_power->bl_prop[inst].min_brightness_millinits) * + core_power->bl_prop[inst].backlight_range, + core_power->bl_prop[inst].nits_range)); + + pwm += core_power->bl_prop[inst].min_backlight_pwm; + + if (pwm > core_power->bl_prop[inst].max_backlight_pwm) + pwm = core_power->bl_prop[inst].max_backlight_pwm; + + return pwm; +} + +static bool validate_ext_backlight_caps( + struct dm_acpi_atif_backlight_caps *ext_backlight_caps) +{ + unsigned int i; + unsigned int num_of_data_points = 0; + unsigned int last_signal_level = 0; + unsigned int last_luminance = 0; + + num_of_data_points = ext_backlight_caps->num_data_points; + + /* Validation rules: + * 1. BIOS should carry customized data points and + * the number of data points should not be larger than 99. + * 2. The max_input_signal should be larger than min_input_signal. + * 3. For each data point: + * a. luminance should be in ascending order and + * should not be 0 or 100 since the corresponding signal_level + * are assigned by min_input_signal and max_input_signal. + * b. signal_level should be in ascending order and + * be within the range of min/max_input_signal. + */ + if (num_of_data_points > BL_DATA_POINTS) + return false; + + if (ext_backlight_caps->min_input_signal >= ext_backlight_caps->max_input_signal) + return false; + + last_signal_level = ext_backlight_caps->min_input_signal; + for (i = 0; i < num_of_data_points; i++) { + unsigned int luminance = ext_backlight_caps->data_points[i].luminance; + unsigned int signal_level = ext_backlight_caps->data_points[i].signal_level; + + if ((luminance <= last_luminance) || (luminance > BL_DATA_POINTS)) + return false; + + if ((signal_level <= last_signal_level) || (signal_level >= ext_backlight_caps->max_input_signal)) + return false; + + last_signal_level = signal_level; + last_luminance = luminance; + } + + return true; +} + +/* hard coded to default backlight curve. */ +static void initialize_backlight_caps(struct core_power *core_power, unsigned int inst) +{ + unsigned int i; + struct dm_acpi_atif_backlight_caps *ext_backlight_caps = NULL; + bool custom_curve_present = false; + unsigned int num_levels = 0; + struct dc *dc = NULL; + enum dm_acpi_display_type acpi_display_type = + (inst == 0) ? AcpiDisplayType_LCD1 : AcpiDisplayType_LCD2; + + if (core_power == NULL) + return; + dc = core_power->dc; + + num_levels = core_power->bl_prop[inst].num_backlight_levels; + + /* Allocate memory for ATIF output + * (do not want to use 256 bytes on the stack) + */ + ext_backlight_caps = (struct dm_acpi_atif_backlight_caps *) + (kzalloc(sizeof(struct dm_acpi_atif_backlight_caps), + GFP_KERNEL)); + + if (ext_backlight_caps == NULL) + return; + + /* Retrieve ACPI extended brightness caps */ + if (dm_query_extended_brightness_caps + (dc->ctx, acpi_display_type, ext_backlight_caps)) { + custom_curve_present = validate_ext_backlight_caps(ext_backlight_caps); + } + + if (core_power->bl_prop[inst].use_custom_backlight_caps && + fill_custom_backlight_caps( + core_power->bl_prop[inst].custom_backlight_caps_config_no, + ext_backlight_caps)) { + custom_curve_present = validate_ext_backlight_caps(ext_backlight_caps); + } + + if (custom_curve_present) { + unsigned int index = 1; + unsigned int num_of_data_points = ext_backlight_caps->num_data_points; + + core_power->bl_prop[inst].ac_backlight_percent = + ext_backlight_caps->ac_level_percentage; + core_power->bl_prop[inst].dc_backlight_percent = + ext_backlight_caps->dc_level_percentage; + core_power->bl_prop[inst].backlight_lut[0] = + backlight_8_to_16( + ext_backlight_caps->min_input_signal); + core_power->bl_prop[inst].backlight_lut[num_levels - 1] = + backlight_8_to_16( + ext_backlight_caps->max_input_signal); + + /* Filling translation table from data points - + * between every two provided data points we + * lineary interpolate missing values + */ + for (i = 0; i < num_of_data_points; i++) { + unsigned int luminance = + ext_backlight_caps->data_points[i].luminance; + unsigned int signal_level = + backlight_8_to_16( + ext_backlight_caps->data_points[i].signal_level); + + /* Since luminance is a percentage, scale it by num_levels*/ + luminance = (luminance * num_levels) / 101; + + /* Lineary interpolate missing values */ + if (index < luminance) { + unsigned int base_value = + core_power->bl_prop[inst].backlight_lut[index-1]; + unsigned int delta_signal = + signal_level - base_value; + unsigned int delta_luma = + luminance - index + 1; + unsigned int step = delta_signal; + + for (; index < luminance; index++) { + core_power->bl_prop[inst].backlight_lut[index] = + base_value + (step / delta_luma); + step += delta_signal; + } + } + + /* Now [index == luminance], + * so we can add data point to the translation table + */ + core_power->bl_prop[inst].backlight_lut[index++] = signal_level; + } + + /* Complete the final segment of interpolation - + * between last datapoint and maximum value + */ + if (index < num_levels - 1) { + unsigned int base_value = + core_power->bl_prop[inst].backlight_lut[index-1]; + unsigned int delta_signal = + core_power->bl_prop[inst].backlight_lut[num_levels - 1] - + base_value; + unsigned int delta_luma = num_levels - index; + unsigned int step = delta_signal; + + for (; index < num_levels - 1; index++) { + core_power->bl_prop[inst].backlight_lut[index] = + base_value + (step / delta_luma); + step += delta_signal; + } + } + /* Build backlight translation table based on default curve */ + } else { + /* Defines default backlight curve F(x) = A(x*x) + Bx + C. + * + * Backlight curve should always satisfy: + * F(0) = min, F(100) = max, + * So polynom coefficients are: + * A is 0.0255 - B/100 - min/10000 - (255-max)/10000 = + * (max - min)/10000 - B/100 + * B is adjustable factor to modify the curve. + * Bigger B results in less concave curve. + * B range is [0..(max-min)/100] + * C is backlight minimum + */ + unsigned int backlight_curve_coeff_a_factor = + num_levels * num_levels; + unsigned int backlight_curve_coeff_b = num_levels; + unsigned int delta = + core_power->bl_prop[inst].backlight_lut[num_levels - 1] - + core_power->bl_prop[inst].backlight_lut[0]; + unsigned int coeffC = core_power->bl_prop[inst].backlight_lut[0]; + unsigned int coeffB = + (backlight_curve_coeff_b < delta ? + backlight_curve_coeff_b : delta); + unsigned long long coeffA = delta - coeffB; /* coeffB is B*100 */ + + for (i = 1; i < num_levels - 1; i++) { + uint64_t lut_val = div_u64(coeffA * i * i, backlight_curve_coeff_a_factor) + + div_u64((uint64_t)coeffB * i, backlight_curve_coeff_b) + coeffC; + + ASSERT(lut_val <= 0xFFFFFFFF); + core_power->bl_prop[inst].backlight_lut[i] = (unsigned int)lut_val; + } + } + + if (ext_backlight_caps != NULL) + kfree(ext_backlight_caps); + + /* Successfully initialized */ + core_power->bl_prop[inst].backlight_caps_valid = true; +} + +static void varibright_set_level(struct core_power *core_power) +{ + if (!core_power->varibright_prop.varibright_active || + !core_power->varibright_prop.varibright_user_enable) + core_power->varibright_prop.varibright_hw_level = 0; + else + core_power->varibright_prop.varibright_hw_level = + core_power->varibright_prop.varibright_level; +} + +bool mod_power_hw_init(struct mod_power *mod_power) +{ + struct core_power *core_power = NULL; + struct dc *dc = NULL; + struct dmcu *dmcu = NULL; + struct dmcu_iram_parameters params; + int i; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + dc = core_power->dc; + + for (i = 0; i < core_power->edp_num; i++) { + params.set = core_power->varibright_prop.varibright_config_setting; + params.backlight_ramping_override = core_power->bl_prop[i].backlight_ramping_override; + params.backlight_ramping_reduction = core_power->bl_prop[i].backlight_ramping_reduction; + params.backlight_ramping_start = core_power->bl_prop[i].backlight_ramping_start; + params.backlight_lut_array = core_power->bl_prop[i].backlight_lut; + params.backlight_lut_array_size = core_power->bl_prop[i].num_backlight_levels; + params.min_abm_backlight = core_power->bl_prop[i].min_abm_backlight; + + dmcu = dc->res_pool->dmcu; + + // In the case where abm is implemented on dmcub, + // dmcu object will be null. + // ABM 2.4 and up are implemented on dmcub. + if (dmcu) { + //DMCU does not support multiple eDP + return dmcu_load_iram(dmcu, params); + } else if (dc->ctx->dmub_srv) { + if (!dmub_init_abm_config(dc->res_pool, params, i)) + return false; + } else + return false; + } + return true; +} + +struct mod_power *mod_power_create(struct dc *dc, + struct mod_power_init_params *init_params, + unsigned int edp_num) +{ + struct core_power *core_power = NULL; + int i = 0; + int abm_max_config = 0; + unsigned int inst = 0; + bool is_brightness_range_valid = false; + + if (dc == NULL) + goto fail_dc_null; + + core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL); + + if (core_power == NULL) + goto fail_alloc_context; + + core_power->edp_num = edp_num; + core_power->map = kzalloc(sizeof(struct power_entity) * MOD_POWER_MAX_CONCURRENT_STREAMS, + GFP_KERNEL); + + if (core_power->map == NULL) + goto fail_alloc_map; + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + core_power->map[i].stream = NULL; + } + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + core_power->map[i].psr_context = + kzalloc(sizeof(struct mod_power_psr_context), + GFP_KERNEL); + if (core_power->map[i].psr_context == NULL) + goto fail_construct; + } + + core_power->psr_smu_optimizations_support = init_params->allow_psr_smu_optimizations; + core_power->multi_disp_optimizations_support = init_params->allow_psr_multi_disp_optimizations; + + for (inst = 0; inst < edp_num; inst++) { + core_power->bl_prop[inst].min_abm_backlight = + init_params[inst].min_abm_backlight; + core_power->bl_prop[inst].disable_fractional_pwm = + init_params[inst].disable_fractional_pwm; + core_power->bl_prop[inst].use_linear_backlight_curve = + init_params[inst].use_linear_backlight_curve; + core_power->bl_prop[inst].use_nits_based_brightness = + init_params[inst].use_nits_based_brightness; + core_power->bl_prop[inst].backlight_ramping_override = + init_params[inst].backlight_ramping_override; + core_power->bl_prop[inst].backlight_ramping_reduction = + init_params[inst].backlight_ramping_reduction; + core_power->bl_prop[inst].backlight_ramping_start = + init_params[inst].backlight_ramping_start; + core_power->bl_prop[inst].use_custom_backlight_caps = + init_params[inst].use_custom_backlight_caps; + core_power->bl_prop[inst].custom_backlight_caps_config_no = + init_params[inst].custom_backlight_caps_config_no; + + // Do not allow less than 101 backlight levels + if (init_params[inst].num_backlight_levels < 101) + core_power->bl_prop[inst].num_backlight_levels = 101; + else + core_power->bl_prop[inst].num_backlight_levels = + init_params[inst].num_backlight_levels; + + core_power->bl_prop[inst].backlight_lut = (unsigned int *) + (kzalloc(sizeof(unsigned int) * + core_power->bl_prop[inst].num_backlight_levels, GFP_KERNEL)); + if (core_power->bl_prop[inst].backlight_lut == NULL) + goto fail_alloc_backlight_array; + } + + core_power->varibright_prop.varibright_active = false; + + core_power->varibright_prop.varibright_user_enable = + init_params->def_varibright_enable; + + // Table of ABM levels here is 1-4, but level 0 also exists as 'off' + if (init_params->varibright_level <= abm_defines_max_level) { + core_power->varibright_prop.varibright_level = + init_params->varibright_level; + + } else { + core_power->varibright_prop.varibright_level = 3; + } + if (init_params->def_varibright_level <= abm_defines_max_level) { + core_power->varibright_prop.def_varibright_level = + init_params->def_varibright_level; + } else { + core_power->varibright_prop.def_varibright_level = 3; + } + + // ABM used to contain 4 different configs. There is only 3 since ABM 2.3. + if ((dc->res_pool->dmcu != NULL) && (dc->res_pool->dmcu->dmcu_version.abm_version < 0x23)) + abm_max_config = 4; + else + abm_max_config = 3; + + if (init_params->abm_config_setting < abm_max_config) + core_power->varibright_prop.varibright_config_setting = + init_params->abm_config_setting; + else + core_power->varibright_prop.varibright_config_setting = 0; + + for (inst = 0; inst < edp_num; inst++) { + core_power->bl_prop[inst].backlight_lut[0] = init_params[inst].min_backlight_pwm; + core_power->bl_prop[inst].backlight_lut[ + core_power->bl_prop[inst].num_backlight_levels-1] = + init_params[inst].max_backlight_pwm; + core_power->bl_prop[inst].min_backlight_pwm = init_params[inst].min_backlight_pwm; + core_power->bl_prop[inst].max_backlight_pwm = init_params[inst].max_backlight_pwm; + core_power->bl_prop[inst].ac_backlight_percent = + default_ac_backlight_percent; + core_power->bl_prop[inst].dc_backlight_percent = + default_dc_backlight_percent; + core_power->bl_prop[inst].backlight_caps_valid = false; + + if (core_power->bl_prop[inst].use_nits_based_brightness) { + core_power->bl_prop[inst].min_brightness_millinits = + init_params[inst].panel_min_millinits; + core_power->bl_prop[inst].max_brightness_millinits = + init_params[inst].panel_max_millinits; + } else { + + core_power->bl_prop[inst].min_brightness_millinits = + pwr_default_min_brightness_millinits; + core_power->bl_prop[inst].max_brightness_millinits = + pwr_default_sdr_brightness_millinits; + } + + core_power->bl_prop[inst].backlight_range = + core_power->bl_prop[inst].max_backlight_pwm- + core_power->bl_prop[inst].min_backlight_pwm; + + core_power->bl_prop[inst].nits_range = + core_power->bl_prop[inst].max_brightness_millinits - + core_power->bl_prop[inst].min_brightness_millinits; + + core_power->bl_state[inst].smooth_brightness_enabled = true; + } + + /* Check if at least 1 instance in core_power is populated before failing */ + for (inst = 0; inst < edp_num; inst++) { + if (core_power->bl_prop[inst].nits_range != 0 && core_power->bl_prop[inst].backlight_range != 0) { + is_brightness_range_valid = true; + break; + } + + } + if (!is_brightness_range_valid) + goto fail_bad_brightness_range; + + core_power->num_entities = 0; + + core_power->dc = dc; + for (inst = 0; inst < edp_num; inst++) { + initialize_backlight_caps(core_power, inst); + core_power->bl_state[inst].backlight_millipercent = + core_power->bl_prop[inst].dc_backlight_percent * 1000; + core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm(core_power, + core_power->bl_state[inst].backlight_millipercent, inst); + core_power->bl_state[inst].backlight_millinit = backlight_millipercent_to_millinit(core_power, + core_power->bl_state[inst].backlight_millipercent, inst); + } + + return &core_power->public; + +fail_bad_brightness_range: +fail_alloc_backlight_array: + for (inst = 0; inst < edp_num; inst++) + if (core_power->bl_prop[inst].backlight_lut) + kfree(core_power->bl_prop[inst].backlight_lut); +fail_construct: + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { + if (core_power->map[i].psr_context) + kfree(core_power->map[i].psr_context); + } + kfree(core_power->map); + +fail_alloc_map: + kfree(core_power); + +fail_alloc_context: +fail_dc_null: + return NULL; +} + +void mod_power_destroy(struct mod_power *mod_power) +{ + if (mod_power != NULL) { + int i; + struct core_power *core_power = + MOD_POWER_TO_CORE(mod_power); + + for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) + if (core_power->map[i].psr_context) + kfree(core_power->map[i].psr_context); + + for (i = 0; i < core_power->num_entities; i++) + if (core_power->map[i].stream) + dc_stream_release(core_power->map[i].stream); + + kfree(core_power->map); + + for (i = 0; i < MAX_NUM_EDP; i++) + if (core_power->bl_prop[i].backlight_lut) + kfree(core_power->bl_prop[i].backlight_lut); + + kfree(core_power); + } +} + +bool mod_power_add_stream(struct mod_power *mod_power, + struct dc_stream_state *stream, struct psr_caps *caps) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities < MOD_POWER_MAX_CONCURRENT_STREAMS) { + dc_stream_retain(stream); + + core_power->map[core_power->num_entities].stream = stream; + core_power->map[core_power->num_entities].caps = caps; + + // initialize cached PSR params to something "safe" (something that is + // consistent with disabled PSR state) + core_power->map[core_power->num_entities].psr_enabled = 0; + core_power->map[core_power->num_entities].psr_events = psr_event_vsync; + core_power->map[core_power->num_entities].psr_power_opt = 0; + core_power->num_entities++; + return true; + } + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: add_stream: ERROR: stream=%p num_entities=%d >= MOD_POWER_MAX_CONCURRENT_STREAMS", + stream, + core_power->num_entities); + + return false; +} + +bool mod_power_remove_stream(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + int i = 0; + struct core_power *core_power = NULL; + unsigned int index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) { + /* trying to remove a stream a second time or have not added yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: remove_stream: ERROR: num_entities=0 stream=%p", + stream); + return false; + } + + index = map_index_from_stream(core_power, stream); + + if (index >= core_power->num_entities) { + /* trying to remove a stream a second time or have not added yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: remove_stream: ERROR: index=%u >= num_entities=%d stream=%p", + index, + core_power->num_entities, + stream); + return false; + } + + dc_stream_release(core_power->map[index].stream); + core_power->map[index].stream = NULL; + /* To remove this entity, shift everything after down */ + for (i = index; i < core_power->num_entities - 1; i++) { + core_power->map[i].stream = core_power->map[i + 1].stream; + core_power->map[i].caps = core_power->map[i + 1].caps; + + // copy over cached parameters in case they map to PSR capable display + core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled; + core_power->map[i].psr_events = core_power->map[i + 1].psr_events; + core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt; + + memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context)); + memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context)); + } + core_power->num_entities--; + + return true; +} + +/* + * Replace_stream should be used when there is a mode set for existing + * display target with a valid stream. In this case might need to retain + * cached PSR state (events, power opt, en/dis) if we are dealing with PSR + * capable display. If mod_power_remove and mod_power_add are used instead, + * then stream may be assigned to a different slot and may end up with + * wrong cached PSR state. It is hard to tell which PSR events should + * persist through mode set or what psr_events should be initialized to, so + * it might be better just to retain them all. + */ +bool mod_power_replace_stream(struct mod_power *mod_power, + const struct dc_stream_state *current_stream, + struct dc_stream_state *new_stream, + struct psr_caps *new_caps) +{ + struct core_power *core_power = NULL; + unsigned int index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) { + /* no streams exist in the table yet */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: replace_stream: ERROR: num_entities=0 stream=%p", + current_stream); + return false; + } + + index = map_index_from_stream(core_power, current_stream); + + if (index >= core_power->num_entities) { + /* trying to replace a non-existent stream */ + BREAK_TO_DEBUGGER(); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power: replace_stream: ERROR: index=%u >= num_entities=%d stream=%p", + index, + core_power->num_entities, + current_stream); + return false; + } + + dc_stream_release(core_power->map[index].stream); + dc_stream_retain(new_stream); + core_power->map[index].stream = new_stream; + core_power->map[index].caps = new_caps; + memset(core_power->map[index].psr_context, 0, sizeof(struct mod_power_psr_context)); + + return true; +} + +static bool set_backlight_millinits_aux(struct core_power *core_power, + struct dc_stream_state *stream, + unsigned int backlight_millinits, + unsigned int transition_time_millisec, + unsigned int inst) +{ + struct dc_link *link = NULL; + + if (core_power == NULL) + return false; + + if (stream == NULL) + return true; + + link = dc_stream_get_link(stream); + + return dc_link_set_backlight_level_nits(link, core_power->bl_state[inst].isHDR, + backlight_millinits, transition_time_millisec); +} + +static bool set_backlight(struct core_power *core_power, + struct dc_stream_state *stream, + struct set_backlight_level_params *backlight_level_params, + unsigned int inst) +{ + bool retv = false; + unsigned int frame_ramp = 0; + unsigned int vsync_rate_hz; + union dmcu_abm_set_bl_params params; + const struct dc_link *link = NULL; + unsigned int backlight_pwm_u16_16 = backlight_level_params->backlight_pwm_u16_16; + unsigned int transition_time_millisec = backlight_level_params->transition_time_in_ms; + + if (core_power == NULL) + return false; + + core_power->bl_state[inst].backlight_pwm = backlight_pwm_u16_16; + + if (stream == NULL) + return true; + + if (stream->link->connector_signal != SIGNAL_TYPE_EDP) + return false; + + if (transition_time_millisec != 0) { + unsigned int v_total = + (stream->adjust.v_total_max == 0) ? stream->timing.v_total : stream->adjust.v_total_max; + + vsync_rate_hz = (unsigned int)div_u64(div_u64((stream-> + timing.pix_clk_100hz * 100), + v_total), + stream->timing.h_total); + + if (core_power->bl_state[inst].smooth_brightness_enabled) + frame_ramp = ((vsync_rate_hz * + transition_time_millisec) + 500) / 1000; + } + + core_power->bl_state[inst].frame_ramp = frame_ramp; + params.u32All = 0; + params.bits.gradual_change = (frame_ramp > 0); + params.bits.frame_ramp = frame_ramp; + link = dc_stream_get_link(stream); + + mod_power_set_psr_event(&core_power->public, stream, true, psr_event_hw_programming, true); + mod_power_set_replay_event(&core_power->public, stream, true, replay_event_hw_programming, true); + + backlight_level_params->frame_ramp = params.u32All; + retv = dc_link_set_backlight_level(link, backlight_level_params); + + mod_power_set_psr_event(&core_power->public, stream, false, psr_event_hw_programming, false); + mod_power_set_replay_event(&core_power->public, stream, false, replay_event_hw_programming, false); + + return retv; +} + +static void fill_backlight_level_params(struct core_power *core_power, + struct set_backlight_level_params *backlight_level_params, + int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm, + enum backlight_control_type backlight_control_type, + unsigned int backlight_millinit, unsigned int transition_time_millisec, + bool is_hdr) +{ + struct backlight_properties *bl_prop = &core_power->bl_prop[panel_inst]; + + backlight_level_params->aux_inst = aux_inst; + backlight_level_params->backlight_pwm_u16_16 = backlight_pwm; + backlight_level_params->control_type = backlight_control_type; + backlight_level_params->backlight_millinits = backlight_millinit; + backlight_level_params->transition_time_in_ms = transition_time_millisec; + backlight_level_params->min_luminance = bl_prop->min_brightness_millinits; + backlight_level_params->max_luminance = bl_prop->max_brightness_millinits; + backlight_level_params->min_backlight_pwm = bl_prop->min_backlight_pwm; + backlight_level_params->max_backlight_pwm = bl_prop->max_backlight_pwm; + + if (backlight_control_type == BACKLIGHT_CONTROL_AMD_AUX && !is_hdr) + backlight_level_params->control_type = BACKLIGHT_CONTROL_PWM; +} + +bool mod_power_set_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int transition_time_millisec, + bool skip_aux, + bool is_hdr) +{ + struct core_power *core_power = NULL; + unsigned int backlight_pwm; + unsigned int panel_inst = 0; + struct set_backlight_level_params backlight_level_params = { 0 }; + const struct dc_link *link = NULL; + uint8_t aux_inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + link = dc_stream_get_link(stream); + + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) + return false; + + if (!skip_aux) { + if (!set_backlight_millinits_aux(core_power, stream, + backlight_millinit, transition_time_millisec, panel_inst)) + return false; + } +// always send both AUX (above) and PWM (below) + core_power->bl_state[panel_inst].backlight_millinit = backlight_millinit; + + core_power->bl_state[panel_inst].backlight_millipercent = + backlight_millinit_to_millipercent( + core_power, backlight_millinit, panel_inst); + + backlight_pwm = backlight_millinit_to_pwm( + core_power, backlight_millinit, panel_inst); + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, aux_inst, backlight_pwm, + link->backlight_control_type, backlight_millinit, transition_time_millisec, is_hdr); + + return set_backlight(core_power, stream, + &backlight_level_params, panel_inst); +} + + +bool mod_power_backlight_percent_to_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int *backlight_millinit) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return false; + + *backlight_millinit = backlight_millipercent_to_millinit( + core_power, backlight_millipercent, inst); + return true; +} + +bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit, + unsigned int *backlight_millipercent) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return false; + + *backlight_millipercent = backlight_millinit_to_millipercent( + core_power, backlight_millinit, inst); + return true; +} + +bool mod_power_set_backlight_percent(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent, + unsigned int transition_time_millisec, + bool is_hdr) +{ + struct core_power *core_power = NULL; + struct set_backlight_level_params backlight_level_params = { 0 }; + const struct dc_link *link = NULL; + unsigned int backlight_pwm; + unsigned int panel_inst = 0; + uint8_t aux_inst = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + link = dc_stream_get_link(stream); + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) + return false; + core_power->bl_state[panel_inst].backlight_millipercent = backlight_millipercent; + + core_power->bl_state[panel_inst].backlight_millinit = + backlight_millipercent_to_millinit( + core_power, backlight_millipercent, panel_inst); + + backlight_pwm = backlight_millipercent_to_pwm( + core_power, backlight_millipercent, panel_inst); + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, + aux_inst, backlight_pwm, link->backlight_control_type, + core_power->bl_state[panel_inst].backlight_millinit, transition_time_millisec, is_hdr); + + return set_backlight(core_power, stream, + &backlight_level_params, panel_inst); +} + +void mod_power_update_backlight(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millipercent) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return; + core_power->bl_state[inst].backlight_millipercent = backlight_millipercent; + + core_power->bl_state[inst].backlight_millinit = + backlight_millipercent_to_millinit( + core_power, backlight_millipercent, inst); + + core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm( + core_power, backlight_millipercent, inst); +} + +void mod_power_update_backlight_nits(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int backlight_millinit) +{ + struct core_power *core_power = NULL; + unsigned int inst = 0; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &inst)) + return; + + core_power->bl_state[inst].backlight_millinit = backlight_millinit; + + core_power->bl_state[inst].backlight_millipercent = backlight_millinit_to_millipercent( + core_power, backlight_millinit, inst); + core_power->bl_state[inst].backlight_pwm = backlight_millinit_to_pwm( + core_power, backlight_millinit, inst); +} + +bool mod_power_get_backlight_pwm(struct mod_power *mod_power, + unsigned int *backlight_pwm, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_pwm = core_power->bl_state[inst].backlight_pwm; + + return true; +} + +bool mod_power_get_backlight_nits(struct mod_power *mod_power, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_millinit = core_power->bl_state[inst].backlight_millinit; + + return true; +} + +bool mod_power_get_backlight_percent(struct mod_power *mod_power, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *backlight_millipercent = core_power->bl_state[inst].backlight_millipercent; + + return true; +} + +bool mod_power_get_hw_target_backlight_pwm_nits(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_target_backlight_pwm(mod_power, link, + &backlight_u16_16)) { + *backlight_millinit = + backlight_pwm_to_millinit(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_target_backlight_pwm_percent(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_target_backlight_pwm(mod_power, link, + &backlight_u16_16)) { + *backlight_millipercent = + backlight_pwm_to_millipercent(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_target_backlight_pwm(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16) +{ + if (mod_power == NULL) + return false; + + *backlight_u16_16 = dc_link_get_target_backlight_pwm(link); + + return true; +} + +bool mod_power_get_hw_backlight_pwm_nits(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millinit, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_backlight_pwm(mod_power, link, &backlight_u16_16)) { + *backlight_millinit = + backlight_pwm_to_millinit(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_backlight_aux_nits(struct mod_power *mod_power, + struct dc_stream_state **streams, int num_streams, + unsigned int *backlight_millinit_avg, + unsigned int *backlight_millinit_peak) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power == NULL) + return false; + + if (num_streams < 1) + return true; + + for (stream_index = 0; stream_index < num_streams; stream_index++) + if (streams[stream_index]->link->connector_signal == SIGNAL_TYPE_EDP || + streams[stream_index]->link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) + break; + + if (stream_index == num_streams) + return false; + + link = dc_stream_get_link(streams[stream_index]); + if (link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 0) + return false; + + return dc_link_get_backlight_level_nits(link, backlight_millinit_avg, + backlight_millinit_peak); +} + +bool mod_power_get_hw_backlight_pwm_percent(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_millipercent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + unsigned int backlight_u16_16 = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (mod_power_get_hw_backlight_pwm(mod_power, link, &backlight_u16_16)) { + *backlight_millipercent = + backlight_pwm_to_millipercent(core_power, + backlight_u16_16, inst); + return true; + } + return false; +} + +bool mod_power_get_hw_backlight_pwm(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int *backlight_u16_16) +{ + if (mod_power == NULL) + return false; + + *backlight_u16_16 = dc_link_get_backlight_level(link); + + return true; +} + +bool mod_power_get_panel_backlight_boundaries( + struct mod_power *mod_power, + unsigned int *out_min_backlight, + unsigned int *out_max_backlight, + unsigned int *out_ac_backlight_percent, + unsigned int *out_dc_backlight_percent, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + /* If cache was successfully updated, + * copy the values to output structure and return success + */ + if (core_power->bl_prop[inst].backlight_caps_valid) { + *out_min_backlight = core_power->bl_prop[inst].backlight_lut[0]; + *out_max_backlight = + core_power->bl_prop[inst].backlight_lut[ + core_power->bl_prop[inst].num_backlight_levels - 1]; + *out_ac_backlight_percent = + core_power->bl_prop[inst].ac_backlight_percent; + *out_dc_backlight_percent = + core_power->bl_prop[inst].dc_backlight_percent; + + return true; + } + + return false; +} + +bool mod_power_set_smooth_brightness(struct mod_power *mod_power, + bool enable_brightness, + unsigned int inst) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + core_power->bl_state[inst].smooth_brightness_enabled = enable_brightness; + + return true; +} + +bool mod_power_notify_mode_change(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool is_hdr) +{ + unsigned int stream_index = 0; + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + struct psr_config psr_config = {0}; + struct psr_context psr_context = {0}; + struct dc *dc = NULL; + unsigned int panel_inst = 0; + int active_psr_events = 0; + int active_replay_events = 0; + + if ((mod_power == NULL) || (stream == NULL)) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (stream_index >= core_power->num_entities) + return false; + + dc = core_power->dc; + link = dc_stream_get_link(stream); + active_psr_events = core_power->map[stream_index].psr_events; + active_replay_events = core_power->map[stream_index].replay_events; + if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) { + struct set_backlight_level_params backlight_level_params = { 0 }; + + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + uint8_t aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; + + if (link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1) + dc_link_set_backlight_level_nits(link, core_power->bl_state[panel_inst].isHDR, + core_power->bl_state[panel_inst].backlight_millinit, 0); + + backlight_level_params.frame_ramp = 0; + + fill_backlight_level_params(core_power, &backlight_level_params, panel_inst, aux_inst, + core_power->bl_state[panel_inst].backlight_pwm, link->backlight_control_type, + core_power->bl_state[panel_inst].backlight_millinit, 0, is_hdr); + + dc_link_set_backlight_level(link, &backlight_level_params); + + mod_power_calc_psr_configs(&psr_config, link, stream); + + psr_config.psr_exit_link_training_required = core_power->map[stream_index].caps->psr_exit_link_training_required; + + if (dc->ctx->asic_id.chip_family >= AMDGPU_FAMILY_GC_11_0_1) + psr_config.allow_smu_optimizations = + core_power->psr_smu_optimizations_support && dc_is_embedded_signal(stream->signal); + else + psr_config.allow_smu_optimizations = + core_power->psr_smu_optimizations_support && mod_power_only_edp(dc->current_state, stream); + + psr_config.allow_multi_disp_optimizations = core_power->multi_disp_optimizations_support; + + psr_config.rate_control_caps = core_power->map[stream_index].caps->rate_control_caps; + + if (active_psr_events & psr_event_os_request_force_ffu) { + psr_config.os_request_force_ffu = true; + } + /* + * DSC support: + * DSC slice height value must be 'mod' by su_y_granularity. + * According to Panel Vendor, there might be varied conditions to fulfill. + * Right now, DSC slice height value must be multiple of su_y_granularity. + * + * The value of DSC slice height is determined in DSC Driver but it does not + * propagated out here, so we need to calculate it as below 'slice_height'. + */ + psr_su_set_dsc_slice_height(dc, link, + (struct dc_stream_state *) stream, + &psr_config); + + dc_link_setup_psr(link, stream, &psr_config, &psr_context); + + link->replay_settings.replay_smu_opt_enable = + (link->replay_settings.config.replay_smu_opt_supported && + mod_power_only_edp(dc->current_state, stream)); + + if (active_replay_events & replay_event_os_request_force_ffu) { + link->replay_settings.config.os_request_force_ffu = true; + } + + if (dc_is_embedded_signal(stream->signal)) + dc->link_srv->dp_setup_replay(link, stream); + } + + return true; +} + +bool mod_power_varibright_feature_enable(struct mod_power *mod_power, bool enable, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_user_enable = enable; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM feature enable: enable=%u su->varibright_level=%u varibright_hw_level=%u", + (unsigned int) enable, + *stream_update->abm_level, + core_power->varibright_prop.varibright_hw_level); + return true; +} + +bool mod_power_varibright_activate(struct mod_power *mod_power, + bool activate, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_active = activate; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM activate: activate=%u su->varibright_level=%u", + (unsigned int) activate, + *stream_update->abm_level); + return true; +} +bool mod_power_varibright_set_level(struct mod_power *mod_power, unsigned int level, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + core_power->varibright_prop.varibright_level = level; + core_power->varibright_prop.varibright_hw_level = level; + + /* find abm hw level to program, and save in stream update */ + varibright_set_level(core_power); + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM set level: level=%u -> (varibright_level=%u varibright_hw_level=%u) -> su->varibright_level=%u", + level, + core_power->varibright_prop.varibright_level, + core_power->varibright_prop.varibright_hw_level, + *stream_update->abm_level); + return true; +} + +bool mod_power_varibright_set_hw_level(struct mod_power *mod_power, unsigned int level, + struct dc_stream_update *stream_update) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (level == 0 || level == ABM_LEVEL_IMMEDIATE_DISABLE) + core_power->varibright_prop.varibright_active = 0; + else + core_power->varibright_prop.varibright_active = 1; + core_power->varibright_prop.varibright_hw_level = level; + *stream_update->abm_level = core_power->varibright_prop.varibright_hw_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">ABM set level: level=%u -> (varibright_level=%u varibright_hw_level=%u) -> su->varibright_level=%u", + level, + core_power->varibright_prop.varibright_level, + core_power->varibright_prop.varibright_hw_level, + *stream_update->abm_level); + return true; +} + +bool mod_power_get_varibright_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.varibright_level; + + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright level: cp->varibright_level=%u", + *varibright_level); + return true; + +} + +bool mod_power_get_varibright_hw_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.varibright_hw_level; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright HW level: hw_level=%u", + *varibright_level); + return true; +} + +bool mod_power_get_varibright_default_level(struct mod_power *mod_power, + unsigned int *varibright_level) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_level = core_power->varibright_prop.def_varibright_level; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright default level: def_varibright_level=%u", + *varibright_level); + return true; +} + +bool mod_power_get_varibright_enable(struct mod_power *mod_power, + bool *varibright_enable) +{ + struct core_power *core_power = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + *varibright_enable = core_power->varibright_prop.varibright_user_enable; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get varibright enable state: varibright_user_enable=%u", + (unsigned int) (*varibright_enable)); + return true; +} + +bool mod_power_is_abm_active(struct mod_power *mod_power, + const struct dc_link *link, + unsigned int inst) +{ + unsigned int user_backlight = 0; + unsigned int current_backlight = 0; + bool is_active = false; + + if (mod_power == NULL) + return false; + + mod_power_get_backlight_pwm(mod_power, &user_backlight, inst); + mod_power_get_hw_backlight_pwm(mod_power, link, ¤t_backlight); + + if (user_backlight != current_backlight) + is_active = true; + else + is_active = false; + DC_TRACE_LEVEL_MESSAGEP(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Backlight_ABM, + ">get ABM active state: is_active=%u (user_backlight_pwm=%u, current_backlight_pwm=%u)", + (unsigned int)is_active, + user_backlight, + current_backlight); + return is_active; +} + + +static void mod_power_psr_set_power_opt(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int active_psr_events, + bool psr_enable_request) +{ + (void)psr_enable_request; + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + unsigned int power_opt = 0; + + if (!stream) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + stream_index = map_index_from_stream(core_power, stream); + if (!core_power->map[stream_index].caps->psr_version) + return; + + link = dc_stream_get_link(stream); + + if (active_psr_events == 0) { + /* Static Screen */ + power_opt |= (psr_power_opt_smu_opt_static_screen | psr_power_opt_z10_static_screen | + psr_power_opt_ds_disable_allow); + } + + /* psr_power_opt_flag is a configuration parameter into the module that determines + * which optimizations to enable during psr + */ + power_opt &= core_power->map[stream_index].caps->psr_power_opt_flag; + if (core_power->map[stream_index].psr_power_opt != power_opt) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_power_opt: psr_power_opt=0x%04x, power_opt=0x%04x" + "active_psr_events=0x%04x, psr_power_opt_flag=0x%04x", + core_power->map[stream_index].psr_power_opt, + power_opt, + active_psr_events, + core_power->map[stream_index].caps->psr_power_opt_flag); + dc_link_set_psr_allow_active(link, NULL, false, false, &power_opt); + core_power->map[stream_index].psr_power_opt = power_opt; + } +} + +static bool set_psr_enable(struct mod_power *mod_power, + struct dc_stream_state *stream, + bool psr_enable, + bool wait, + bool force_static) +{ + struct core_power *core_power = NULL; + enum dc_psr_state state = PSR_STATE0; + unsigned int retry_count; + const unsigned int max_retry = 1000; + struct dc_link *link = NULL; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: stream=%p num_entities=%d", + stream, + core_power->num_entities); + return false; + } + + if (psr_enable) { + unsigned int vsync_rate_hz; + struct dc_static_screen_params params = {0}; + + vsync_rate_hz = (unsigned int)div_u64(div_u64(( + stream->timing.pix_clk_100hz * 100), + stream->timing.v_total), + stream->timing.h_total); + + params.triggers.cursor_update = true; + params.triggers.overlay_update = true; + params.triggers.surface_update = true; + params.num_frames = calc_psr_num_static_frames(vsync_rate_hz); + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: CALCS: pix_clk_100hz=%u v_total=%u h_total=%u vsync_rate_hz=%u num_frames=%u", + stream->timing.pix_clk_100hz, + stream->timing.v_total, + stream->timing.h_total, + vsync_rate_hz, + params.num_frames); + + dc_stream_set_static_screen_params(core_power->dc, + &stream, 1, + ¶ms); + } + + link = dc_stream_get_link(stream); + + if (!dc_link_set_psr_allow_active(link, &psr_enable, false, force_static, NULL)) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: stream=%p link=%p psr_enable=%d", + stream, + link, + psr_enable); + return false; + } + + if (wait == true) { + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: BEGIN WAIT: psr_enable=%d", + (int)psr_enable); + + for (retry_count = 0; retry_count <= max_retry; retry_count++) { + dc_link_get_psr_state(link, &state); + if (psr_enable) { + if (state != PSR_STATE0 && + (!force_static || state == PSR_STATE3)) + break; + } else { + if (state == PSR_STATE0) + break; + } + udelay(500); + } + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: END WAIT: psr_enable=%d", + (int)psr_enable); + + /* assert if max retry hit */ + if (retry_count >= max_retry) { + ASSERT(0); + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: ERROR: retry_count=%u: Unexpectedly long wait for PSR state change.", + retry_count); + } + } else { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION, + WPP_BIT_FLAG_Firmware_PsrState, + "set psr enable: PSR state change initiated (wait=false): psr_enable=%d", + (int)psr_enable); + } + + return true; +} + +bool mod_power_get_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_psr_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + *active_psr_events = core_power->map[stream_index].psr_events; + + return true; +} + +bool mod_power_set_psr_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum psr_event event, bool wait) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + unsigned int active_psr_events = 0; + bool psr_enable_request = false; + bool force_static = false; + + if (mod_power == NULL || stream == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + stream_index = map_index_from_stream(core_power, stream); + + if (core_power->num_entities == 0) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_psr_event: ERROR: stream=%p event=%d num_entities=%d", + stream, + (int)event, + core_power->num_entities); + return false; + } + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + if (set_event) + core_power->map[stream_index].psr_events |= event; + else + core_power->map[stream_index].psr_events &= ~event; + + active_psr_events = core_power->map[stream_index].psr_events; + + // ignore other events when we're in forced psr enabled state + if (active_psr_events & psr_event_dynamic_display_switch && + event != psr_event_dynamic_display_switch) + return false; + + // ignore other events when we're in forced psr enabled state + if (active_psr_events & psr_event_os_override_hold && + event != psr_event_os_override_hold) + return false; + + // ignore other events when we're in forced psr enabled state + // dds events need to be processed while in dynamic_link_rate_control + if (active_psr_events & psr_event_dynamic_link_rate_control && + event != psr_event_dynamic_link_rate_control && + event != psr_event_dds_defer_stream_enable && + event != psr_event_dynamic_display_switch) + return false; + + if (active_psr_events & (psr_event_test_harness_disable_psr | psr_event_os_request_disable)) + psr_enable_request = false; + else if (active_psr_events & psr_event_pause) + psr_enable_request = false; + else if (active_psr_events & psr_event_test_harness_enable_psr) + psr_enable_request = true; + else if (active_psr_events & psr_event_dynamic_display_switch) { + psr_enable_request = true; + force_static = true; + } else if (active_psr_events & psr_event_dynamic_link_rate_control) { + psr_enable_request = true; + force_static = true; + } else if (active_psr_events & psr_event_edp_panel_off_disable_psr) + psr_enable_request = false; + else if (active_psr_events & (psr_event_hw_programming | + psr_event_defer_enable | + psr_event_dds_defer_stream_enable | + psr_event_vrr_transition | + psr_event_immediate_flip)) + psr_enable_request = false; + else if (active_psr_events & psr_event_big_screen_video) + psr_enable_request = true; + else if (active_psr_events & psr_event_full_screen) + psr_enable_request = false; + else if (active_psr_events & psr_event_mpo_video_selective_update) + psr_enable_request = true; + else if (active_psr_events & psr_event_vsync) + psr_enable_request = false; + else if (active_psr_events & psr_event_crc_window_active) + psr_enable_request = false; + else + psr_enable_request = true; + + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE, + WPP_BIT_FLAG_Firmware_PsrState, + "mod_power set_psr_event: before: psr_enabled=%d -> request: set_event=%d event=0x%04x -> result: psr_events=0x%04x psr_enable_request=%d", + (int)core_power->map[stream_index].psr_enabled, + (int)set_event, + (unsigned int)event, + (unsigned int)core_power->map[stream_index].psr_events, + (int)psr_enable_request); + mod_power_psr_set_power_opt(mod_power, stream, active_psr_events, psr_enable_request); + + if (core_power->map[stream_index].psr_enabled != psr_enable_request || force_static) { + if (set_psr_enable(mod_power, stream, psr_enable_request, wait, force_static)) { + core_power->map[stream_index].psr_enabled = psr_enable_request; + } + } + + return true; +} + +bool mod_power_get_psr_state(struct mod_power *mod_power, + const struct dc_stream_state *stream, + enum dc_psr_state *state) +{ + struct core_power *core_power = NULL; + const struct dc_link *link = NULL; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + link = dc_stream_get_link(stream); + return dc_link_get_psr_state(link, state); +} + +bool mod_power_get_psr_enabled_status(struct mod_power *mod_power, + const struct dc_stream_state *stream, + bool *psr_enabled) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + *psr_enabled = core_power->map[stream_index].psr_enabled; + + return true; +} + +void mod_power_psr_residency(struct mod_power *mod_power, + const struct dc_stream_state *stream, + unsigned int *residency, + const uint8_t mode) +{ + struct core_power *core_power = NULL; + const struct dc_link *link = NULL; + + if (!stream) + return; + + if (mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return; + + link = dc_stream_get_link(stream); + + if (link != NULL) + link->dc->link_srv->edp_get_psr_residency(link, residency, mode); +} +bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int *active_psr_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + if (active_psr_events == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + *active_psr_events = core_power->map[stream_index].psr_events; + return true; +} + +bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint16_t psr_vtotal_idle, + uint16_t psr_vtotal_su) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + const struct dc_link *link = NULL; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (!core_power->map[stream_index].caps->psr_version) + return false; + + link = dc_stream_get_link(stream); + + return link->dc->link_srv->edp_set_sink_vtotal_in_psr_active( + link, psr_vtotal_idle, psr_vtotal_su); +} + +static bool mod_power_set_replay_active(struct dc_stream_state *stream, + bool replay_active, + bool wait, + bool force_static) +{ + uint64_t state; + unsigned int retry_count; + const unsigned int max_retry = 1000; + struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + + if (!link) + return false; + + if (!dc_link_set_replay_allow_active(link, &replay_active, false, force_static, NULL)) + return false; + + if (wait == true) { + + for (retry_count = 0; retry_count <= max_retry; retry_count++) { + dc_link_get_replay_state(link, &state); + if (replay_active) { + if (state != REPLAY_STATE_0 && + (!force_static || state == REPLAY_STATE_3)) + break; + } else { + if (state == REPLAY_STATE_0) + break; + } + udelay(500); + } + + /* assert if max retry hit */ + if (retry_count >= max_retry) + ASSERT(0); + } else { + /* To-do: Add trace log */ + } + + return true; +} + +static unsigned int mod_power_replay_setup_power_opt(struct dc_link *link, + unsigned int active_replay_events, bool is_ultra_sleep_mode) +{ + unsigned int power_opt = 0; + + if (is_ultra_sleep_mode) { + /* Static Screen */ + power_opt |= (replay_power_opt_smu_opt_static_screen | replay_power_opt_z10_static_screen); + } else if (active_replay_events & replay_event_test_harness_ultra_sleep) { + power_opt |= replay_power_opt_z10_static_screen; + } + + /* replay_power_opt_flag is a configuration parameter into the module that determines + * which optimizations to enable during replay + */ + power_opt &= link->replay_settings.config.replay_power_opt_supported; + + return power_opt; +} + +static bool mod_power_replay_set_power_opt(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int active_replay_events, + bool is_ultra_sleep_mode) +{ + (void)mod_power; + struct dc_link *link = NULL; + unsigned int power_opt = 0; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode); + + if (!dc_link_set_replay_allow_active(link, NULL, false, false, &power_opt)) + return false; + + return true; +} + +bool mod_power_get_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, + unsigned int *active_replay_events) +{ + struct core_power *core_power = NULL; + unsigned int stream_index = 0; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + *active_replay_events = core_power->map[stream_index].replay_events; + + return true; +} + +static bool mod_power_update_replay_active_status(unsigned int active_replay_events, + struct dc_link *link, uint32_t *coasting_vtotal, bool *is_full_screen_video, bool *is_ultra_sleep_mode, uint16_t *frame_skip_number, bool *is_video_playback) +{ + if (!link || !coasting_vtotal || !is_full_screen_video || !is_video_playback) + return false; + + // Check coasting_vtotal_table has been updated. + if (!link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC] + || !link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]) + return false; + + unsigned int replay_enable_option = + link->replay_settings.config.replay_enable_option; + + /* TODO: To support test harness and DDS event */ + + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF); + *frame_skip_number = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + link->replay_settings.config.replay_timing_sync_supported = false; + + *is_full_screen_video = false; + + *is_ultra_sleep_mode = false; + + *is_video_playback = false; + + /* DSAT test scenario */ + if (active_replay_events & replay_event_test_harness_mode) { + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF); + *frame_skip_number = + (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + } + + /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */ + if (active_replay_events & (replay_event_test_harness_enable_replay)) { + if ((active_replay_events & replay_event_test_harness_ultra_sleep) && + !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) + link->replay_settings.config.replay_timing_sync_supported = false; + return true; + } else + return false; + } else if (active_replay_events & (replay_event_test_harness_enable_replay)) { + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + + /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */ + if ((active_replay_events & replay_event_test_harness_ultra_sleep) && + !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) + link->replay_settings.config.replay_timing_sync_supported = false; + return true; + } else if (active_replay_events & (replay_event_test_harness_disable_replay | replay_event_os_request_disable)) { + // set last set coasting vtotal + if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; + if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + return false; + } + + /* Inactive conditions */ + if (active_replay_events & (replay_event_edp_panel_off_disable_psr | + replay_event_hw_programming | + replay_event_vrr | + replay_event_immediate_flip | + replay_event_prepare_vtotal | + replay_event_vrr_transition | + replay_event_pause | + replay_event_disable_replay_while_DPMS | + replay_event_sleep_resume | + replay_event_disable_in_AC | + replay_event_disable_replay_while_detect_display | + replay_event_infopacket | + replay_event_crc_window_active)) + return false; + + // Full screen scenario + if (active_replay_events & replay_event_full_screen) { + if (!(replay_enable_option & pr_enable_option_full_screen)) + return false; + } + + /* Full screen video scenario */ + if (active_replay_events & replay_event_big_screen_video) { + + link->replay_settings.config.replay_timing_sync_supported = false; + + if (replay_enable_option & pr_enable_option_full_screen_video_coasting) { + unsigned int fsn_vid = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO]; + + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO]; + ASSERT(fsn_vid <= 0xFFFF); + *frame_skip_number = (uint16_t)fsn_vid; + } + + *is_video_playback = true; + + if ((replay_enable_option & pr_enable_option_full_screen_video) && + (replay_enable_option & pr_enable_option_full_screen_video_coasting)) { + *is_full_screen_video = true; + return true; + } else + return false; + } + + /* MPO video scenario + * Some of the cases may contain a full screen UI layer in MPO video scenario which is + * not the expected case to enable Replay. + */ + if ((active_replay_events & replay_event_mpo_video_selective_update) && + !(active_replay_events & replay_event_full_screen)) { + + link->replay_settings.config.replay_timing_sync_supported = false; + + if (replay_enable_option & pr_enable_option_mpo_video_coasting) { + *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + } + + *is_video_playback = true; + + if (replay_enable_option & pr_enable_option_mpo_video) + return true; + else + return false; + } + + /* Static screen scenario */ + if (!(active_replay_events & replay_event_vsync)) { + + if (replay_enable_option & pr_enable_option_static_screen_coasting) { + // Do not adjust eDP refresh rate if static screen + normal sleep mode + if ((!(link->replay_settings.config.replay_power_opt_supported & + replay_power_opt_z10_static_screen)) || + (active_replay_events & replay_event_cursor_updating)) { + // normal sleep mode + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + } else { + // ultra sleep mode + *coasting_vtotal = + link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC]; + { + uint32_t frame_skip_val = + link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_STATIC]; + + ASSERT(frame_skip_val <= 0xFFFF); + *frame_skip_number = (uint16_t)frame_skip_val; + } + *is_ultra_sleep_mode = true; + } + } + + if (replay_enable_option & pr_enable_option_static_screen) { + if (!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) + link->replay_settings.config.replay_timing_sync_supported = false; + return true; + } else + return false; + } + + /* General UI scenario */ + if (active_replay_events & replay_event_general_ui) { + if (replay_enable_option & pr_enable_option_general_ui) + return true; + else + return false; + } + + return false; +} + +bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, + uint32_t coasting_vtotal, + uint16_t frame_skip_number) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + return link->dc->link_srv->edp_set_coasting_vtotal(link, coasting_vtotal, frame_skip_number); +} + +void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.sync_data.timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported; + + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Timing_Sync_Supported, + &cmd_data); +} + +void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.disabled_adaptive_sync_sdp_data.force_disabled = force_disabled; + + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Disabled_Adaptive_Sync_SDP, + &cmd_data); +} + +static void mod_power_replay_set_general_cmd(struct mod_power *mod_power, + const struct dc_stream_state *stream, + const enum dmub_cmd_replay_general_subtype general_cmd_type, + const uint32_t param1, const uint32_t param2) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.set_general_cmd_data.subtype = general_cmd_type; + cmd_data.set_general_cmd_data.param1 = param1; + cmd_data.set_general_cmd_data.param2 = param2; + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_General_Cmd, + &cmd_data); +} + +void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool force_disabled) +{ + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION, + force_disabled, 0); +} + +static void mod_power_replay_set_pseudo_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint16_t vtotal) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + union dmub_replay_cmd_set cmd_data = { 0 }; + + if (!stream || mod_power == NULL) + return; + + core_power = MOD_POWER_TO_CORE(mod_power); + if (core_power->num_entities == 0) + return; + + stream_index = map_index_from_stream(core_power, stream); + if (stream_index > core_power->num_entities) //invalid index + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + cmd_data.pseudo_vtotal_data.vtotal = vtotal; + + if (link->replay_settings.last_pseudo_vtotal != vtotal) { + link->replay_settings.last_pseudo_vtotal = vtotal; + link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Pseudo_VTotal, &cmd_data); + } +} + +static void mod_power_update_error_status(struct mod_power *mod_power, + const struct dc_stream_state *stream) +{ + struct dc_link *link = NULL; + union replay_debug_flags *pDebug = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + + if (!link) + return; + + pDebug = (union replay_debug_flags *)&link->replay_settings.config.debug_flags; + + if (0 == pDebug->bitfields.enable_visual_confirm_debug) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS, + link->replay_settings.config.replay_error_status.raw, 0); +} + +void mod_power_set_low_rr_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool low_rr_supported) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + + if (!link) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE, + low_rr_supported, 0); +} + +void mod_power_set_video_conferencing_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool video_conferencing_activate) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_VIDEO_CONFERENCING, + video_conferencing_activate, 0); +} + +void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power, + const struct dc_stream_state *stream, uint32_t coasting_vtotal) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_COASTING_VTOTAL_WITHOUT_FRAME_UPDATE, + coasting_vtotal, 0); +} + +void mod_power_set_replay_continuously_resync(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool enable) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_SET_CONTINUOUSLY_RESYNC, + enable, 0); +} + +void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power, + const struct dc_stream_state *stream, bool live_capture_with_cvt_activate) +{ + struct dc_link *link = NULL; + + if (mod_power == NULL || stream == NULL) + return; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return; + + // Check if LIVE_CAPTURE_WITH_CVT bit is enabled in DalRegKey_ReplayOptimization + if (!link->replay_settings.config.replay_optimization.bits.LIVE_CAPTURE_WITH_CVT) + return; + + if (link->replay_settings.config.live_capture_with_cvt_activated != live_capture_with_cvt_activate) { + link->replay_settings.config.live_capture_with_cvt_activated = live_capture_with_cvt_activate; + mod_power_replay_set_general_cmd(mod_power, stream, + REPLAY_GENERAL_CMD_LIVE_CAPTURE_WITH_CVT, + live_capture_with_cvt_activate, 0); + } +} + +bool mod_power_set_replay_event(struct mod_power *mod_power, + struct dc_stream_state *stream, bool set_event, + enum replay_event event, bool wait_for_disable) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int stream_index = 0; + unsigned int active_replay_events = 0; + bool replay_active_request = false; + bool force_static = false; + uint32_t coasting_vtotal = 0; + bool current_timing_sync_status = false; + bool is_full_screen_video = false; + bool is_ultra_sleep_mode = false; + unsigned int sink_duration_us = 0; + bool low_rr_active = false; + uint16_t frame_skip_number = 0; + bool is_video_playback = false; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + stream_index = map_index_from_stream(core_power, stream); + + if (set_event) + core_power->map[stream_index].replay_events |= event; + else + core_power->map[stream_index].replay_events &= ~event; + + link = dc_stream_get_link(stream); + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + if ((core_power->map[stream_index].replay_events & replay_event_disable_replay_while_switching_mux) != 0) + return false; + + if ((core_power->map[stream_index].replay_events & replay_event_os_override_hold) != 0) + return false; + + active_replay_events = core_power->map[stream_index].replay_events; + + current_timing_sync_status = + link->replay_settings.config.replay_timing_sync_supported; + + replay_active_request = mod_power_update_replay_active_status(active_replay_events, + link, &coasting_vtotal, &is_full_screen_video, &is_ultra_sleep_mode, &frame_skip_number, &is_video_playback); + + if (is_full_screen_video) + mod_power_replay_set_pseudo_vtotal(mod_power, stream, + link->replay_settings.low_rr_full_screen_video_pseudo_vtotal); + else + mod_power_replay_set_pseudo_vtotal(mod_power, stream, 0); + + //If timing_sync_status change, then re-enabled set timing_sync_supported value and re-enabled replay + if (current_timing_sync_status != link->replay_settings.config.replay_timing_sync_supported) + mod_power_replay_set_timing_sync_supported(mod_power, stream); + + if (link->replay_settings.config.low_rr_supported) { + sink_duration_us = + (unsigned int)(div_u64(((unsigned long long)(coasting_vtotal) + * 10000) * stream->timing.h_total, + stream->timing.pix_clk_100hz)); + low_rr_active = sink_duration_us < LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND ? false : true; + if (low_rr_active != link->replay_settings.config.low_rr_activated) { + mod_power_set_low_rr_activate(mod_power, stream, low_rr_active); + link->replay_settings.config.low_rr_activated = low_rr_active; + } + } + + // The function return fail when + // 1. DMUB function is not support (for backward compatible). + // 2. active_replay_events or coasting_vtotal is not updated in the same time + if (!mod_power_replay_set_power_opt_and_coasting_vtotal(mod_power, + stream, active_replay_events, coasting_vtotal, is_ultra_sleep_mode, frame_skip_number)) { + if (!mod_power_replay_set_power_opt(mod_power, stream, active_replay_events, is_ultra_sleep_mode)) + return false; + + if (!mod_power_replay_set_coasting_vtotal(mod_power, stream, coasting_vtotal, frame_skip_number)) + return false; + } + + mod_power_set_live_capture_with_cvt_activate(mod_power, stream, is_video_playback); + + mod_power_update_error_status(mod_power, stream); + + // If Replay is going to be enable (No matter is disable -> enable or enable -> enable), we don't need to wait. + // If Replay is going to be disable + // if disable -> disable + // -> Replay DMUB state should be state 0. + // So no matter wait_for_disable is true or not, it should makes no difference. + // if enable -> disable -> We should wait if wait_for_disable is true. + if (replay_active_request) + wait_for_disable = false; + + if (!mod_power_set_replay_active(stream, replay_active_request, wait_for_disable, force_static)) + return false; + + return true; +} + +bool mod_power_get_replay_active_status(const struct dc_stream_state *stream, + bool *replay_active) +{ + const struct dc_link *link = NULL; + + if (!stream) + return false; + + link = dc_stream_get_link(stream); + *replay_active = link->replay_settings.replay_allow_active; + + return true; +} + +void mod_power_replay_residency(const struct dc_stream_state *stream, + unsigned int *residency, const bool is_start, const bool is_alpm) +{ + const struct dc_link *link = NULL; + enum pr_residency_mode mode; + + if (!stream) + return; + + link = dc_stream_get_link(stream); + + if (is_alpm) + mode = PR_RESIDENCY_MODE_ALPM; + else + mode = PR_RESIDENCY_MODE_PHY; + + if (link && link->dc && link->dc->link_srv) + link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode); +} + +bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power, + const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal, + bool is_ultra_sleep_mode, uint16_t frame_skip_number) +{ + struct core_power *core_power = NULL; + struct dc_link *link = NULL; + unsigned int power_opt = 0; + + if (!stream) + return false; + + if (mod_power == NULL) + return false; + + core_power = MOD_POWER_TO_CORE(mod_power); + + if (core_power->num_entities == 0) + return false; + + link = dc_stream_get_link(stream); + + if (!link || !link->replay_settings.replay_feature_enabled) + return false; + + power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode); + + return link->dc->link_srv->edp_set_replay_power_opt_and_coasting_vtotal(link, &power_opt, coasting_vtotal, frame_skip_number); +} + + + + + -- 2.43.0