From patchwork Tue Feb 16 22:28:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harry Wentland X-Patchwork-Id: 8332981 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id D6E6CC02AA for ; Tue, 16 Feb 2016 22:30:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2BA59202FE for ; Tue, 16 Feb 2016 22:30:14 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 3493F2034C for ; Tue, 16 Feb 2016 22:30:07 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 00D606E8EB; Tue, 16 Feb 2016 22:29:02 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from na01-by2-obe.outbound.protection.outlook.com (mail-by2on0078.outbound.protection.outlook.com [207.46.100.78]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9333C6E8C2 for ; Tue, 16 Feb 2016 22:28:54 +0000 (UTC) Received: from CY1PR12CA0063.namprd12.prod.outlook.com (10.163.230.31) by SN1PR12MB0863.namprd12.prod.outlook.com (10.164.27.13) with Microsoft SMTP Server (TLS) id 15.1.409.15; Tue, 16 Feb 2016 22:28:51 +0000 Received: from BY2NAM03FT023.eop-NAM03.prod.protection.outlook.com (2a01:111:f400:7e4a::209) by CY1PR12CA0063.outlook.office365.com (2a01:111:e400:c42b::31) with Microsoft SMTP Server (TLS) id 15.1.409.15 via Frontend Transport; Tue, 16 Feb 2016 22:28:51 +0000 Authentication-Results: spf=none (sender IP is 165.204.84.221) smtp.mailfrom=amd.com; lists.freedesktop.org; dkim=none (message not signed) header.d=none;lists.freedesktop.org; dmarc=permerror action=none header.from=amd.com; Received-SPF: None (protection.outlook.com: amd.com does not designate permitted sender hosts) Received: from atltwp01.amd.com (165.204.84.221) by BY2NAM03FT023.mail.protection.outlook.com (10.152.84.226) with Microsoft SMTP Server id 15.1.415.6 via Frontend Transport; Tue, 16 Feb 2016 22:28:50 +0000 X-WSS-ID: 0O2NVS0-07-L6F-02 X-M-MSG: Received: from satlvexedge02.amd.com (satlvexedge02.amd.com [10.177.96.29]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by atltwp01.amd.com (Axway MailGate 5.3.1) with ESMTPS id 2209ECAE804 for ; Tue, 16 Feb 2016 17:28:47 -0500 (EST) Received: from SATLEXCHOV01.amd.com (10.181.40.71) by SATLVEXEDGE02.amd.com (10.177.96.29) with Microsoft SMTP Server (TLS) id 14.3.195.1; Tue, 16 Feb 2016 16:28:51 -0600 Received: from STOREXDAG02.amd.com (10.1.13.11) by SATLEXCHOV01.amd.com (10.181.40.71) with Microsoft SMTP Server (TLS) id 14.3.266.1; Tue, 16 Feb 2016 16:28:47 -0600 Received: from cnhwentlanub.amd.com (172.29.225.36) by storexdag02.amd.com (10.1.13.11) with Microsoft SMTP Server id 14.3.266.1; Tue, 16 Feb 2016 17:28:46 -0500 From: Harry Wentland To: Subject: [PATCH v2 21/26] drm/amd/dal: Add Carrizo HW sequencer and resource Date: Tue, 16 Feb 2016 17:28:01 -0500 Message-ID: X-Mailer: git-send-email 2.1.4 In-Reply-To: References: <1455211209-26733-1-git-send-email-harry.wentland@amd.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-Forefront-Antispam-Report: CIP:165.204.84.221; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10009020)(979002)(6009001)(2980300002)(428002)(189002)(199003)(2950100001)(19580395003)(19580405001)(50226001)(48376002)(5003600100002)(5008740100001)(1096002)(106466001)(105586002)(50466002)(11100500001)(229853001)(86362001)(2351001)(450100001)(77096005)(1220700001)(36756003)(47776003)(189998001)(87936001)(110136002)(5003940100001)(92566002)(118296001)(53416004)(33646002)(2906002)(586003)(76176999)(5890100001)(50986999)(101416001)(4326007)(959014)(579004)(569005); DIR:OUT; SFP:1101; SCL:1; SRVR:SN1PR12MB0863; H:atltwp01.amd.com; FPR:; SPF:None; MLV:ovrnspm; MX:1; A:1; PTR:InfoDomainNonexistent; LANG:en; X-MS-Office365-Filtering-Correlation-Id: c54fc0d2-29e1-4102-184a-08d337208b53 X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0863; 2:uEGeGvslcAC5qeaZyCexLTJjMaW8xQYtMuIjHlpD4RAlnrc1VKyAgflpa6/jAaVltrPqx0BTlpQ/XMzjsET1jrM8tleas0rz+iX/c16TArVroJggzfrmSRonAeqvXyNF+VKMTkcM2bcGzlSRRlS4Nsgw8Hasz2UUpwJFBHq1AJPS9+PRG2PvMdcJyb69J1ht; 3:d9J/x4An7iSAL2G1gwDhfz9hKWA47f6qDMbABUGf4JXKbOc4V0VyfKAngIS+bNJxFxEAv0MmF28KUpngZNGOkaeY3jbwES2808vrAwD1psOmKkqaOOYzmwWREh75/ka20StMml6swOv60L8Gtb/fa05BK2I/wGirwROFsaiU3SPr5s/ePxqRTFcpPOXVC1b4C2jI3pkaIk6eTEVxGxVkETJ2SrF6sh2kHlf4eYHDncE=; 25:lso1Y97otNSfB1uEp1gcZbURGkwvaoz0SLeabbrPpjLVJqkZD2v2hEIE9QaLxKage9gw68HEZMyfyfywWKNwnhgghGmkCPWq3a+h2O/rvmh60vYA1mJMASfX8tUxqgixNDgdozb1VYjbChiEWxL7JwOyxaKOoOBmhBBL907p5V/JRPE2Gsx7NfXU/7BDADC+Fvs4BSykFKb7uQxhMiTU/KGtMLDugHqo/MDLMrKa4N0EudfgM3UIfUrIm0kJnbJEkjOPjVEgDNOhQ+Vfw0ZSimbpH/PAvIg0l3/+242Hf7aMcAi2fGZsBvxv30cPgEBP X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SN1PR12MB0863; X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0863; 20:OsjEhVkZohX0Y97ky9AXRAR9p8MbjxhS90O9fbu4m5gMYeaARU4duhFOF2jikEJN3aoYjeTIuUxlEa3wBXCb4z5a2cu73WPFxanVGNU2+9VJ6My1lOqbV9wpfUV7n3XtplCiA4w/GiuL5YkXkSzDsJ0yW2h87G9Sp6nNLrkt4NxPFJCGDjLmxnRH3ooK+kE225O6X5PPdAjGqY9va/h0fXqqN/SE+6h6lvzuEwyDcWkFnUTrjNCsHO9XEJEBpkTKCxgvTDMh0p2IKK39OVEc+njiNNf+EYRcVh1o6dtW/7F/vGpxy+V437e3Fwys/zvB6ibOLjagRMADlM994JGNGhemffa4n4oF4JjNP+VnXOQgZFj8djru+hpJeUYmX/MjxqWVpwlpbUW7pjpWMYhOx488kf7vrCuk9dZ7dUc+CUwEoC/Y1qBX7CCrTrSemK4SQVlEIMV5azUM1FCPWUdC/WdPr2ucEstXePT5kuW9ASKxDxsavYrEoIkczyj11MGN X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(767451399110); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(13017025)(13015025)(5005006)(13018025)(13023025)(13024025)(8121501046)(10201501046)(3002001); SRVR:SN1PR12MB0863; BCL:0; PCL:0; RULEID:; SRVR:SN1PR12MB0863; X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0863; 4:mB67gha6KwyI29FCPQtCAjg/aZzKQCbjpbojJO4zz+CJLhg825cfQ2KAPS1TBYCCFuyRcWCA247fb4AkFL7wsrLe9tkoXsiCNT30MiGy1GmmXTMFqbm2UO3yaJ3VoUKoGfyhHBC2jt2bqaAlpF4+MkABkpKmALg6JhpDmhVK0FH2D6wYlGBI9RTzbzPjqf7lMHBo9rjhs47+cB0nQ/A45yMw2ZN3mtbdFd3M8Y6o8R8uLxzK2/LpQG/9r/vcJTbZVlD0YF6fYUKZL0we+6IEHTIkL+2KRcZqf2z0NI4BW3UPMeT/8AGlzVDWIZT1U3dfd2N2OC8ZaJ2Vnfvh6HfygHx75MbskXe6FqZ65lLLCgewiQCbFMYAsF4dR9FDNNpRKnMfGB6RbqKA3yV0auTsmKTNv71GBpPggWYzvcpjw1Qhc2Nt22V4dLIiAW7FsOba1PrMRDSqCtO6VPcIVLgEknfU4Y6yi9RyNXpjRHwptDtOWVUS9zyRoigZV2jZK+Ey X-Forefront-PRVS: 0854128AF0 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; SN1PR12MB0863; 23:tkVDlzOg7cpxHSM2YFe8otIlXpRajj69cRLWvxShc?= =?us-ascii?Q?DYwz/+7QAp5vTG3eixWpCt2MRbaPpgSggb2L38kP7Nfo+JBpkcKyP4nkdEN/?= =?us-ascii?Q?Pe4EiSIKJXtIgaDEyu2tsnAJjRPFdJDeLPKzUzZyREw7FzG4AUAk4j6QrFGi?= =?us-ascii?Q?n0XWvp+tZYqRsxg1B8jxq/mj2GaiqOrmN/O9idE1p0qbZu4RJYyELuLhrQmM?= =?us-ascii?Q?FpYZrtPZt7z8czAhM19kop6ts971VTRZd7j41NaSZ80aUQJLhHrgnWb7OKZ4?= =?us-ascii?Q?IEfA7QHlCLhUB9CnhXFqh5uEYBifFxC8ee9nFKpJvQB5uLTe7SskuzsSEeEC?= =?us-ascii?Q?MTNQEFbolEHgFGUF+7E2x3RrMT+c1toOcTs08rym9SXmU7SXP1o7JrdVaVCu?= =?us-ascii?Q?9x6hEAn5ct4GEyARxVk6pr146RtKcM5zwxrX9dDf9WaQ7lHsh595S5YYVcBu?= =?us-ascii?Q?5jRLqPUsnV8V2ANvgE/Kbr/z4JZh3lIWtaDUW5T8aaxNirOOBO4fkz1+4dsg?= =?us-ascii?Q?TxyhzFSL5Q2llPik8b26RAoiT0h0TLX7Ida5PH15etB8HCnzFVJzkIJ1DJqA?= =?us-ascii?Q?UvWufMztwswRiQSxUd2G59VIgtw69CFSmepQ4uAblTKuMSiaNauNBbP4IVeb?= =?us-ascii?Q?T6nsjLuEojLQcJccj/RAdBVFSUQOass1LGOR3WqmlDu34T0bcsgHWQZRS6SW?= =?us-ascii?Q?OXNDo2oeU/hSS6Nnqr/ISMuaLSVf5uI+FFkc63SVZLUzhP8vCeDllkZUx4jH?= =?us-ascii?Q?+NYWoGCxQ5KaZdRE7W1L97TKCaIaGetk8240Qx5ae4sBGrNR4IASGPF10NGv?= =?us-ascii?Q?Xcidu8wWcqf9zXE5kQ7uvk3QzCJ+uCU37dK0Nn2T0W8aeSOwG0NiVBGL4zr/?= =?us-ascii?Q?Mc7jSiuulOmEHu2NO2K+d632xTZxbgL2IBr2GJO2+Ku8phpCoRizoQPS+UJY?= =?us-ascii?Q?uGykVraxaBHVx1/irjVCrBoK2Jgk96e8SpHFep6F237FUi91kRvNAXpzYN53?= =?us-ascii?Q?scKyxcWctudkWF99oHu3pVjzOHRBwPDPQW1okbWvtenoqM8QEtbTfVQZD5IH?= =?us-ascii?Q?ZXA6vmwRBtcfJjXxcdkX9+7JPQX78UtTYVyLUadQwTS6dBcYdwpLXGXUGpAW?= =?us-ascii?Q?7GWQq47344=3D?= X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0863; 5:LNExknU5g018EvELnJAPhN/t5m+Z1UCBI4IQ6QHuO7oQEe/dDmhbpof6d927aUkb96Z1LCJY9X8691g5ne4KE9NCOWhZPnz1ISDPKzIS9UsyJ/9kxydGWyCEHY1bYmKv86nqv7VFaThJ6fdSgcptew==; 24:fEA6tjjLKgnsCk6k7X1AQ+rInU44nJOMDmv/q3HA/fCNdTPLz4tH1caulaYNMaOSqRCju3W/FjcbIocSmAEG7IlMQipusOaPgw65dfRyh6Y=; 20:sE6Jtri8tgHd5up9nPkrorDJvNzl8YhV4tpMPwjh+5bWv3XocKpMquUn5hyt967C5MGUsE7KTMdRKkOKcuT/9SX8Amwhn3USHROonoM54LM1NquObcTQ0//ZK7Vu/OUjdf0XP+EZg0UZFJ29psLZqlJFPpDqh71pHLSD17khuoOjhDT+yTU9dBMW9vUdUT0qkM7JA6Xim2SmBXOTyWGPNR48Bjj2G2upoPLgOKbDwc3xrjYeJcd9o0Mj9rG40j4n SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Feb 2016 22:28:50.0019 (UTC) 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.221]; Helo=[atltwp01.amd.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN1PR12MB0863 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Adds dce110_resource and dce110_hw_sequencer files. dce110_resource manages creation of HW resources, along with correct ASIC register offset for each block. dce110_hw_sequencers is responsible for programming HW sequences, such as enable_stream, program_scaler, power_down_encoders, etc. Signed-off-by: Harry Wentland Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/dal/dc/dce110/Makefile | 15 + .../drm/amd/dal/dc/dce110/dce110_hw_sequencer.c | 1658 ++++++++++++++++++++ .../drm/amd/dal/dc/dce110/dce110_hw_sequencer.h | 36 + .../gpu/drm/amd/dal/dc/dce110/dce110_resource.c | 1238 +++++++++++++++ .../gpu/drm/amd/dal/dc/dce110/dce110_resource.h | 46 + 5 files changed, 2993 insertions(+) create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/Makefile create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/Makefile b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile new file mode 100644 index 000000000000..ae9d2de92da2 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the 'controller' sub-component of DAL. +# It provides the control and status of HW CRTC block. + +DCE110 = dce110_ipp.o dce110_ipp_cursor.o \ +dce110_ipp_gamma.o dce110_link_encoder.o dce110_opp.o \ +dce110_opp_formatter.o dce110_opp_regamma.o dce110_stream_encoder.o \ +dce110_timing_generator.o dce110_transform.o dce110_transform_gamut.o \ +dce110_transform_scl.o dce110_transform_sclv.o dce110_opp_csc.o\ +dce110_compressor.o dce110_mem_input.o dce110_hw_sequencer.o \ +dce110_resource.o dce110_transform_bit_depth.o dce110_clock_source.o + +AMD_DAL_DCE110 = $(addprefix $(AMDDALPATH)/dc/dce110/,$(DCE110)) + +AMD_DAL_FILES += $(AMD_DAL_DCE110) diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c new file mode 100644 index 000000000000..71fa7b1f8061 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.c @@ -0,0 +1,1658 @@ +/* + * Copyright 2015 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 "dc_bios_types.h" +#include "core_types.h" +#include "core_status.h" +#include "resource.h" +#include "hw_sequencer.h" +#include "dm_helpers.h" +#include "dce110_hw_sequencer.h" + +#include "gpu/dce110/dc_clock_gating_dce110.h" + +#include "timing_generator.h" +#include "mem_input.h" +#include "opp.h" +#include "ipp.h" +#include "transform.h" +#include "stream_encoder.h" +#include "link_encoder.h" +#include "clock_source.h" + +/* include DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +struct dce110_hw_seq_reg_offsets { + uint32_t dcfe; + uint32_t blnd; + uint32_t crtc; +}; + +enum pipe_lock_control { + PIPE_LOCK_CONTROL_GRAPHICS = 1 << 0, + PIPE_LOCK_CONTROL_BLENDER = 1 << 1, + PIPE_LOCK_CONTROL_SCL = 1 << 2, + PIPE_LOCK_CONTROL_SURFACE = 1 << 3, + PIPE_LOCK_CONTROL_MODE = 1 << 4 +}; + +enum blender_mode { + BLENDER_MODE_CURRENT_PIPE = 0,/* Data from current pipe only */ + BLENDER_MODE_OTHER_PIPE, /* Data from other pipe only */ + BLENDER_MODE_BLENDING,/* Alpha blending - blend 'current' and 'other' */ + BLENDER_MODE_STEREO +}; + +static const struct dce110_hw_seq_reg_offsets reg_offsets[] = { +{ + .dcfe = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .blnd = (mmBLND0_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC0_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .dcfe = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .blnd = (mmBLND1_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC1_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +}, +{ + .dcfe = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .blnd = (mmBLND2_BLND_CONTROL - mmBLND_CONTROL), + .crtc = (mmCRTC2_CRTC_GSL_CONTROL - mmCRTC_GSL_CONTROL), +} +}; + +#define HW_REG_DCFE(reg, id)\ + (reg + reg_offsets[id].dcfe) + +#define HW_REG_BLND(reg, id)\ + (reg + reg_offsets[id].blnd) + +#define HW_REG_CRTC(reg, id)\ + (reg + reg_offsets[id].crtc) + + +/******************************************************************************* + * Private definitions + ******************************************************************************/ +/***************************PIPE_CONTROL***********************************/ +static void dce110_enable_fe_clock( + struct dc_context *ctx, uint8_t controller_id, bool enable) +{ + uint32_t value = 0; + uint32_t addr; + + /*TODO: proper offset*/ + addr = HW_REG_DCFE(mmDCFE_CLOCK_CONTROL, controller_id); + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + enable, + DCFE_CLOCK_CONTROL, + DCFE_CLOCK_ENABLE); + + dm_write_reg(ctx, addr, value); +} + +static void dce110_init_pte(struct dc_context *ctx) +{ + uint32_t addr; + uint32_t value = 0; + uint32_t chunk_int = 0; + uint32_t chunk_mul = 0; + + addr = mmUNP_DVMM_PTE_CONTROL; + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + 0, + DVMM_PTE_CONTROL, + DVMM_USE_SINGLE_PTE); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE0); + + set_reg_field_value( + value, + 1, + DVMM_PTE_CONTROL, + DVMM_PTE_BUFFER_MODE1); + + dm_write_reg(ctx, addr, value); + + addr = mmDVMM_PTE_REQ; + value = dm_read_reg(ctx, addr); + + chunk_int = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + chunk_mul = get_reg_field_value( + value, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + if (chunk_int != 0x4 || chunk_mul != 0x4) { + + set_reg_field_value( + value, + 255, + DVMM_PTE_REQ, + MAX_PTEREQ_TO_ISSUE); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_INT); + + set_reg_field_value( + value, + 4, + DVMM_PTE_REQ, + HFLIP_PTEREQ_PER_CHUNK_MULTIPLIER); + + dm_write_reg(ctx, addr, value); + } +} + +/* this is a workaround for hw bug - it is a trigger on r/w */ +static void trigger_write_crtc_h_blank_start_end( + struct dc_context *ctx, + uint8_t controller_id) +{ + uint32_t value; + uint32_t addr; + + addr = HW_REG_CRTC(mmCRTC_H_BLANK_START_END, controller_id); + value = dm_read_reg(ctx, addr); + dm_write_reg(ctx, addr, value); +} + +static bool dce110_pipe_control_lock( + struct dc_context *ctx, + uint8_t controller_idx, + uint32_t control_mask, + bool lock) +{ + uint32_t addr = HW_REG_BLND(mmBLND_V_UPDATE_LOCK, controller_idx); + uint32_t value = dm_read_reg(ctx, addr); + bool need_to_wait = false; + + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SCL) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_SCL_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_DCP_GRPH_SURF_V_UPDATE_LOCK); + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_BLND_V_UPDATE_LOCK); + need_to_wait = true; + } + + if (control_mask & PIPE_LOCK_CONTROL_MODE) + set_reg_field_value( + value, + lock, + BLND_V_UPDATE_LOCK, + BLND_V_UPDATE_LOCK_MODE); + + dm_write_reg(ctx, addr, value); + + if (!lock && need_to_wait) { + uint8_t counter = 0; + const uint8_t counter_limit = 100; + const uint16_t delay_us = 1000; + + uint8_t pipe_pending; + + addr = HW_REG_BLND(mmBLND_REG_UPDATE_STATUS, + controller_idx); + + while (counter < counter_limit) { + value = dm_read_reg(ctx, addr); + + pipe_pending = 0; + + if (control_mask & PIPE_LOCK_CONTROL_BLENDER) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + BLND_BLNDC_UPDATE_PENDING); + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + BLND_BLNDO_UPDATE_PENDING); + } + + if (control_mask & PIPE_LOCK_CONTROL_SCL) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDC_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + SCL_BLNDO_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_GRAPHICS) { + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDC_GRPH_UPDATE_PENDING); + pipe_pending |= + get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDO_GRPH_UPDATE_PENDING); + } + if (control_mask & PIPE_LOCK_CONTROL_SURFACE) { + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDC_GRPH_SURF_UPDATE_PENDING); + pipe_pending |= get_reg_field_value( + value, + BLND_REG_UPDATE_STATUS, + DCP_BLNDO_GRPH_SURF_UPDATE_PENDING); + } + + if (pipe_pending == 0) + break; + + counter++; + dm_delay_in_microseconds(ctx, delay_us); + } + + if (counter == counter_limit) { + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: wait for update exceeded (wait %d us)\n", + __func__, + counter * delay_us); + dal_logger_write( + ctx->logger, + LOG_MAJOR_WARNING, + LOG_MINOR_COMPONENT_CONTROLLER, + "%s: control %d, remain value %x\n", + __func__, + control_mask, + value); + } else { + /* OK. */ + } + } + + if (!lock && (control_mask & PIPE_LOCK_CONTROL_BLENDER)) + trigger_write_crtc_h_blank_start_end(ctx, controller_idx); + + return true; +} + +static void dce110_set_blender_mode( + struct dc_context *ctx, + uint8_t controller_id, + uint32_t mode) +{ + uint32_t value; + uint32_t addr = HW_REG_BLND(mmBLND_CONTROL, controller_id); + uint32_t blnd_mode; + uint32_t feedthrough = 0; + + switch (mode) { + case BLENDER_MODE_OTHER_PIPE: + feedthrough = 0; + blnd_mode = 1; + break; + case BLENDER_MODE_BLENDING: + feedthrough = 0; + blnd_mode = 2; + break; + case BLENDER_MODE_CURRENT_PIPE: + default: + feedthrough = 1; + blnd_mode = 0; + break; + } + + value = dm_read_reg(ctx, addr); + + set_reg_field_value( + value, + feedthrough, + BLND_CONTROL, + BLND_FEEDTHROUGH_EN); + + set_reg_field_value( + value, + blnd_mode, + BLND_CONTROL, + BLND_MODE); + + dm_write_reg(ctx, addr, value); +} + +static void dce110_crtc_switch_to_clk_src( + struct clock_source *clk_src, uint8_t crtc_inst) +{ + uint32_t pixel_rate_cntl_value; + uint32_t addr; + + addr = mmCRTC0_PIXEL_RATE_CNTL + crtc_inst * + (mmCRTC1_PIXEL_RATE_CNTL - mmCRTC0_PIXEL_RATE_CNTL); + + pixel_rate_cntl_value = dm_read_reg(clk_src->ctx, addr); + + if (clk_src->id == CLOCK_SOURCE_ID_EXTERNAL) + set_reg_field_value(pixel_rate_cntl_value, 1, + CRTC0_PIXEL_RATE_CNTL, DP_DTO0_ENABLE); + else { + set_reg_field_value(pixel_rate_cntl_value, + 0, + CRTC0_PIXEL_RATE_CNTL, + DP_DTO0_ENABLE); + + set_reg_field_value(pixel_rate_cntl_value, + clk_src->id - 1, + CRTC0_PIXEL_RATE_CNTL, + CRTC0_PIXEL_RATE_SOURCE); + } + dm_write_reg(clk_src->ctx, addr, pixel_rate_cntl_value); +} +/**************************************************************************/ + +static void enable_display_pipe_clock_gating( + struct dc_context *ctx, + bool clock_gating) +{ + /*TODO*/ +} + +static bool dce110_enable_display_power_gating( + struct dc_context *ctx, + uint8_t controller_id, + struct dc_bios *dcb, + enum pipe_gating_control power_gating) +{ + enum bp_result bp_result = BP_RESULT_OK; + enum bp_pipe_control_action cntl; + + if (IS_FPGA_MAXIMUS_DC(ctx->dce_environment)) + return true; + + if (power_gating == PIPE_GATING_CONTROL_INIT) + cntl = ASIC_PIPE_INIT; + else if (power_gating == PIPE_GATING_CONTROL_ENABLE) + cntl = ASIC_PIPE_ENABLE; + else + cntl = ASIC_PIPE_DISABLE; + + if (!(power_gating == PIPE_GATING_CONTROL_INIT && controller_id != 0)) + bp_result = dcb->funcs->enable_disp_power_gating( + dcb, controller_id + 1, cntl); + + if (power_gating != PIPE_GATING_CONTROL_ENABLE) + dce110_init_pte(ctx); + + if (bp_result == BP_RESULT_OK) + return true; + else + return false; +} + + +static bool set_gamma_ramp( + struct input_pixel_processor *ipp, + struct output_pixel_processor *opp, + const struct gamma_ramp *ramp, + const struct gamma_parameters *params) +{ + /*Power on LUT memory*/ + opp->funcs->opp_power_on_regamma_lut(opp, true); + + + if (params->surface_pixel_format == PIXEL_FORMAT_INDEX8 || + params->selected_gamma_lut == GRAPHICS_GAMMA_LUT_LEGACY) { + /* do legacy DCP for 256 colors if we are requested to do so */ + ipp->funcs->ipp_set_legacy_input_gamma_ramp( + ipp, ramp, params); + + ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, true); + + /* set bypass */ + ipp->funcs->ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED); + + ipp->funcs->ipp_set_degamma(ipp, params, true); + + opp->funcs->opp_set_regamma(opp, ramp, params, true); + } else if (params->selected_gamma_lut == + GRAPHICS_GAMMA_LUT_LEGACY_AND_REGAMMA) { + if (!opp->funcs->opp_map_legacy_and_regamma_hw_to_x_user( + opp, ramp, params)) { + BREAK_TO_DEBUGGER(); + /* invalid parameters or bug */ + return false; + } + + /* do legacy DCP for 256 colors if we are requested to do so */ + ipp->funcs->ipp_set_legacy_input_gamma_ramp( + ipp, ramp, params); + + ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, true); + + /* set bypass */ + ipp->funcs->ipp_program_prescale(ipp, PIXEL_FORMAT_UNINITIALIZED); + } else { + ipp->funcs->ipp_set_legacy_input_gamma_mode(ipp, false); + + ipp->funcs->ipp_program_prescale(ipp, params->surface_pixel_format); + + /* Do degamma step : remove the given gamma value from FB. + * For FP16 or no degamma do by pass */ + ipp->funcs->ipp_set_degamma(ipp, params, false); + + opp->funcs->opp_set_regamma(opp, ramp, params, false); + } + + /*re-enable low power mode for LUT memory*/ + opp->funcs->opp_power_on_regamma_lut(opp, false); + + return true; +} + +static enum dc_status bios_parser_crtc_source_select( + struct core_stream *stream) +{ + struct dc_bios *dcb; + /* call VBIOS table to set CRTC source for the HW + * encoder block + * note: video bios clears all FMT setting here. */ + struct bp_crtc_source_select crtc_source_select = {0}; + const struct core_sink *sink = stream->sink; + + crtc_source_select.engine_id = stream->stream_enc->id; + crtc_source_select.controller_id = stream->controller_idx + 1; + /*TODO: Need to un-hardcode color depth, dp_audio and account for + * the case where signal and sink signal is different (translator + * encoder)*/ + crtc_source_select.signal = sink->public.sink_signal; + crtc_source_select.enable_dp_audio = false; + crtc_source_select.sink_signal = sink->public.sink_signal; + crtc_source_select.display_output_bit_depth = PANEL_8BIT_COLOR; + + dcb = dal_adapter_service_get_bios_parser(sink->link->adapter_srv); + + if (BP_RESULT_OK != dcb->funcs->crtc_source_select( + dcb, + &crtc_source_select)) { + return DC_ERROR_UNEXPECTED; + } + + return DC_OK; +} + +static enum color_space surface_color_to_color_space( + struct plane_colorimetry *colorimetry) +{ + enum color_space color_space = COLOR_SPACE_UNKNOWN; + + switch (colorimetry->color_space) { + case SURFACE_COLOR_SPACE_SRGB: + case SURFACE_COLOR_SPACE_XRRGB: + if (colorimetry->limited_range) + color_space = COLOR_SPACE_SRGB_LIMITED_RANGE; + else + color_space = COLOR_SPACE_SRGB_FULL_RANGE; + break; + case SURFACE_COLOR_SPACE_BT601: + case SURFACE_COLOR_SPACE_XVYCC_BT601: + color_space = COLOR_SPACE_YCBCR601; + break; + case SURFACE_COLOR_SPACE_BT709: + case SURFACE_COLOR_SPACE_XVYCC_BT709: + color_space = COLOR_SPACE_YCBCR709; + break; + } + + return color_space; +} + +/*******************************FMT**************************************/ +static void program_fmt( + struct output_pixel_processor *opp, + struct bit_depth_reduction_params *fmt_bit_depth, + struct clamping_and_pixel_encoding_params *clamping) +{ + /* dithering is affected by , hence should be + * programmed afterwards */ + + opp->funcs->opp_program_bit_depth_reduction( + opp, + fmt_bit_depth); + + opp->funcs->opp_program_clamping_and_pixel_encoding( + opp, + clamping); + + return; +} + +static void update_bios_scratch_critical_state(struct adapter_service *as, + bool state) +{ + struct dc_bios *dcb = dal_adapter_service_get_bios_parser(as); + + dcb->funcs->set_scratch_critical_state(dcb, state); +} + +static void update_info_frame(struct core_stream *stream) +{ + if (dc_is_hdmi_signal(stream->signal)) + stream->stream_enc->funcs->update_hdmi_info_packets( + stream->stream_enc, + &stream->encoder_info_frame); + else if (dc_is_dp_signal(stream->signal)) + stream->stream_enc->funcs->update_dp_info_packets( + stream->stream_enc, + &stream->encoder_info_frame); +} + + +static void enable_stream(struct core_stream *stream) +{ + enum lane_count lane_count = + stream->sink->link->cur_link_settings.lane_count; + + struct dc_crtc_timing *timing = &stream->public.timing; + struct core_link *link = stream->sink->link; + + /* 1. update AVI info frame (HDMI, DP) + * we always need to update info frame + */ + uint32_t active_total_with_borders; + uint32_t early_control = 0; + struct timing_generator *tg = stream->tg; + + update_info_frame(stream); + /* enable early control to avoid corruption on DP monitor*/ + active_total_with_borders = + timing->h_addressable + + timing->h_border_left + + timing->h_border_right; + + if (lane_count != 0) + early_control = active_total_with_borders % lane_count; + + if (early_control == 0) + early_control = lane_count; + + tg->funcs->set_early_control(tg, early_control); + + /* enable audio only within mode set */ + if (stream->audio != NULL) { + dal_audio_enable_output( + stream->audio, + stream->stream_enc->id, + stream->signal); + } + + /* For MST, there are multiply stream go to only one link. + * connect DIG back_end to front_end while enable_stream and + * disconnect them during disable_stream + * BY this, it is logic clean to separate stream and link */ + link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc, + stream->stream_enc->id, true); + +} + +static void disable_stream(struct core_stream *stream) +{ + struct core_link *link = stream->sink->link; + + if (dc_is_hdmi_signal(stream->signal)) + stream->stream_enc->funcs->stop_hdmi_info_packets( + stream->stream_enc); + + if (dc_is_dp_signal(stream->signal)) + stream->stream_enc->funcs->stop_dp_info_packets( + stream->stream_enc); + + if (stream->audio) { + /* mute audio */ + dal_audio_mute(stream->audio, stream->stream_enc->id, + stream->signal); + + /* TODO: notify audio driver for if audio modes list changed + * add audio mode list change flag */ + /* dal_audio_disable_azalia_audio_jack_presence(stream->audio, + * stream->stream_engine_id); + */ + } + + /* blank at encoder level */ + if (dc_is_dp_signal(stream->signal)) + stream->stream_enc->funcs->dp_blank(stream->stream_enc); + + link->link_enc->funcs->connect_dig_be_to_fe( + link->link_enc, + stream->stream_enc->id, + false); + +} + +static void unblank_stream(struct core_stream *stream, + struct link_settings *link_settings) +{ + struct encoder_unblank_param params = { { 0 } }; + + /* only 3 items below are used by unblank */ + params.crtc_timing.pixel_clock = + stream->public.timing.pix_clk_khz; + params.link_settings.link_rate = link_settings->link_rate; + stream->stream_enc->funcs->dp_unblank( + stream->stream_enc, ¶ms); +} + +static enum color_space get_output_color_space( + const struct dc_crtc_timing *dc_crtc_timing) +{ + enum color_space color_space = COLOR_SPACE_SRGB_FULL_RANGE; + + switch (dc_crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + case PIXEL_ENCODING_YCBCR444: + case PIXEL_ENCODING_YCBCR420: + { + if ((dc_crtc_timing->timing_standard == + TIMING_STANDARD_CEA770) || + (dc_crtc_timing->timing_standard == + TIMING_STANDARD_CEA861)) { + if (dc_crtc_timing->pix_clk_khz > 27030) { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR709_YONLY; + else + color_space = COLOR_SPACE_YCBCR709; + } else { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR601_YONLY; + else + color_space = COLOR_SPACE_YCBCR601; + } + } + } + break; + + default: + break; + } + + return color_space; +} + +static enum dc_status apply_single_controller_ctx_to_hw(uint8_t controller_idx, + struct validate_context *context, + const struct dc *dc) +{ + struct core_stream *stream = + context->res_ctx.controller_ctx[controller_idx].stream; + struct output_pixel_processor *opp = + context->res_ctx.pool.opps[controller_idx]; + bool timing_changed = context->res_ctx.controller_ctx[controller_idx] + .flags.timing_changed; + enum color_space color_space; + + if (timing_changed) { + /* Must blank CRTC after disabling power gating and before any + * programming, otherwise CRTC will be hung in bad state + */ + stream->tg->funcs->set_blank(stream->tg, true); + + core_link_disable_stream(stream->sink->link, stream); + + /*TODO: AUTO check if timing changed*/ + if (false == stream->clock_source->funcs->program_pix_clk( + stream->clock_source, + &stream->pix_clk_params, + &stream->pll_settings)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + stream->tg->funcs->program_timing( + stream->tg, + &stream->public.timing, + true); + } + + /*TODO: mst support - use total stream count*/ + stream->mi->funcs->mem_input_allocate_dmif_buffer( + stream->mi, + &stream->public.timing, + context->target_count); + + if (timing_changed) { + if (false == stream->tg->funcs->enable_crtc( + stream->tg)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + } + + /* TODO: move to stream encoder */ + if (stream->signal != SIGNAL_TYPE_VIRTUAL) + if (DC_OK != bios_parser_crtc_source_select(stream)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + + opp->funcs->opp_set_dyn_expansion( + opp, + COLOR_SPACE_YCBCR601, + stream->public.timing.display_color_depth, + stream->sink->public.sink_signal); + + program_fmt(opp, &stream->bit_depth_params, &stream->clamping); + + stream->sink->link->link_enc->funcs->setup( + stream->sink->link->link_enc, + stream->signal); + + if (dc_is_dp_signal(stream->signal)) + stream->stream_enc->funcs->dp_set_stream_attribute( + stream->stream_enc, + &stream->public.timing); + + if (dc_is_hdmi_signal(stream->signal)) + stream->stream_enc->funcs->hdmi_set_stream_attribute( + stream->stream_enc, + &stream->public.timing, + stream->audio != NULL); + + if (dc_is_dvi_signal(stream->signal)) + stream->stream_enc->funcs->dvi_set_stream_attribute( + stream->stream_enc, + &stream->public.timing, + (stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? + true : false); + + if (stream->audio != NULL) { + if (AUDIO_RESULT_OK != dal_audio_setup( + stream->audio, + &stream->audio_output, + &stream->public.audio_info)) { + BREAK_TO_DEBUGGER(); + return DC_ERROR_UNEXPECTED; + } + } + + /* Setup audio rate clock source */ + if (stream->audio != NULL) + dal_audio_setup_audio_wall_dto( + stream->audio, + stream->signal, + &stream->audio_output.crtc_info, + &stream->audio_output.pll_info); + + /* program blank color */ + color_space = get_output_color_space(&stream->public.timing); + stream->tg->funcs->set_blank_color( + context->res_ctx.pool.timing_generators[controller_idx], + color_space); + + if (timing_changed) + core_link_enable_stream(stream->sink->link, stream); + + if (dc_is_dp_signal(stream->signal)) + unblank_stream(stream, &stream->sink->link->cur_link_settings); + + return DC_OK; +} + + +/******************************************************************************/ + +static void power_down_encoders(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->link_count; i++) { + dc->links[i]->link_enc->funcs->disable_output( + dc->links[i]->link_enc, SIGNAL_TYPE_NONE); + } +} + +static void power_down_controllers(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->res_pool.controller_count; i++) { + dc->res_pool.timing_generators[i]->funcs->disable_crtc( + dc->res_pool.timing_generators[i]); + } +} + +static void power_down_clock_sources(struct dc *dc) +{ + int i; + + for (i = 0; i < dc->res_pool.clk_src_count; i++) { + if (dc->res_pool.clock_sources[i]->funcs->cs_power_down( + dc->res_pool.clock_sources[i]) == false) + dm_error("Failed to power down pll! (clk src index=%d)\n", i); + } +} + +static void power_down_all_hw_blocks(struct dc *dc) +{ + power_down_encoders(dc); + + power_down_controllers(dc); + + power_down_clock_sources(dc); +} + +static void disable_vga_and_power_gate_all_controllers( + struct dc *dc) +{ + int i; + struct timing_generator *tg; + struct dc_bios *dcb; + struct dc_context *ctx; + + dcb = dal_adapter_service_get_bios_parser( + dc->res_pool.adapter_srv); + + for (i = 0; i < dc->res_pool.controller_count; i++) { + tg = dc->res_pool.timing_generators[i]; + ctx = dc->ctx; + + tg->funcs->disable_vga(tg); + + /* Enable CLOCK gating for each pipe BEFORE controller + * powergating. */ + enable_display_pipe_clock_gating(ctx, + true); + dc->hwss.enable_display_power_gating(ctx, i, dcb, + PIPE_GATING_CONTROL_ENABLE); + } +} + +/** + * When ASIC goes from VBIOS/VGA mode to driver/accelerated mode we need: + * 1. Power down all DC HW blocks + * 2. Disable VGA engine on all controllers + * 3. Enable power gating for controller + * 4. Set acc_mode_change bit (VBIOS will clear this bit when going to FSDOS) + */ +static void enable_accelerated_mode(struct dc *dc) +{ + struct dc_bios *dcb; + + dcb = dal_adapter_service_get_bios_parser(dc->res_pool.adapter_srv); + + power_down_all_hw_blocks(dc); + + disable_vga_and_power_gate_all_controllers(dc); + + dcb->funcs->set_scratch_acc_mode_change(dcb); +} + +#if 0 +static enum clocks_state get_required_clocks_state( + struct display_clock *display_clock, + struct state_dependent_clocks *req_state_dep_clks) +{ + enum clocks_state clocks_required_state; + enum clocks_state dp_link_required_state; + enum clocks_state overall_required_state; + + clocks_required_state = dal_display_clock_get_required_clocks_state( + display_clock, req_state_dep_clks); + + dp_link_required_state = CLOCKS_STATE_ULTRA_LOW; + + /* overall required state is the max of required state for clocks + * (pixel, display clock) and the required state for DP link. */ + overall_required_state = + clocks_required_state > dp_link_required_state ? + clocks_required_state : dp_link_required_state; + + /* return the min required state */ + return overall_required_state; +} + +static bool dc_pre_clock_change( + struct dc_context *ctx, + struct minimum_clocks_calculation_result *min_clk_in, + enum clocks_state required_clocks_state, + struct power_to_dal_info *output) +{ + struct dal_to_power_info input = {0}; + + input.min_deep_sleep_sclk = min_clk_in->min_deep_sleep_sclk; + input.min_mclk = min_clk_in->min_mclk_khz; + input.min_sclk = min_clk_in->min_sclk_khz; + + switch (required_clocks_state) { + case CLOCKS_STATE_ULTRA_LOW: + input.required_clock = PP_CLOCKS_STATE_ULTRA_LOW; + break; + case CLOCKS_STATE_LOW: + input.required_clock = PP_CLOCKS_STATE_LOW; + break; + case CLOCKS_STATE_NOMINAL: + input.required_clock = PP_CLOCKS_STATE_NOMINAL; + break; + case CLOCKS_STATE_PERFORMANCE: + input.required_clock = PP_CLOCKS_STATE_PERFORMANCE; + break; + default: + input.required_clock = PP_CLOCKS_STATE_NOMINAL; + break; + } + + if (!dc_service_pp_pre_dce_clock_change(ctx, &input, output)) { + dm_error("DC: dc_service_pp_pre_dce_clock_change failed!\n"); + return false; + } + + return true; +} + +static bool dc_set_clocks_and_clock_state ( + struct validate_context *context) +{ + struct power_to_dal_info output = {0}; + + struct display_clock *disp_clk = context->res_ctx.pool.display_clock; + struct dc_context *ctx = context->targets[0]->ctx; + + + if (!dc_pre_clock_change( + ctx, + &context->res_ctx.min_clocks, + get_required_clocks_state( + context->res_ctx.pool.display_clock, + &context->res_ctx.state_clocks), + &output)) { + /* "output" was not updated by PPLib. + * DAL will use default values for set mode. + * + * Do NOT fail this call. */ + return true; + } + + /* PPLib accepted the "clock state" that we need, that means we + * can store it as minimum state because PPLib guarantees not go below + * that state. + * + * Update the clock state here (prior to setting Pixel clock, + * or Display clock) + **/ + if (!dal_display_clock_set_min_clocks_state( + disp_clk, context->res_ctx.required_clocks_state)) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to set minimum clock state!\n"); + } + + + /*bm_clk_info.max_mclk_khz = output.max_mclk; + bm_clk_info.min_mclk_khz = output.min_mclk; + bm_clk_info.max_sclk_khz = output.max_sclk; + bm_clk_info.min_sclk_khz = output.min_sclk;*/ + + /* Now let Bandwidth Manager know about values we got from PPLib. */ + /*dal_bandwidth_manager_set_dynamic_clock_info(bw_mgr, &bm_clk_info);*/ + + return true; +} +#endif + +/** + * Call display_engine_clock_dce80 to perform the Dclk programming. + */ +static void set_display_clock(struct validate_context *context) +{ + /* Program the display engine clock. + * Check DFS bypass mode support or not. DFSbypass feature is only when + * BIOS GPU info table reports support. */ + + if (/*dal_adapter_service_is_dfs_bypass_enabled()*/ false) { + /*TODO: set_display_clock_dfs_bypass( + hws, + path_set, + context->res_ctx.pool.display_clock, + context->res_ctx.min_clocks.min_dclk_khz);*/ + } else + dal_display_clock_set_clock(context->res_ctx.pool.display_clock, + context->bw_results.dispclk_khz); + + /* TODO: When changing display engine clock, DMCU WaitLoop must be + * reconfigured in order to maintain the same delays within DMCU + * programming sequences. */ + + /* TODO: Start GTC counter */ +} + +static void set_displaymarks( + const struct dc *dc, struct validate_context *context) +{ + uint8_t i, j; + uint8_t total_streams = 0; + uint8_t target_count = context->target_count; + + for (i = 0; i < target_count; i++) { + struct core_target *target = context->targets[i]; + + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + + stream->mi->funcs->mem_input_program_display_marks( + stream->mi, + context->bw_results + .nbp_state_change_wm_ns[total_streams], + context->bw_results + .stutter_exit_wm_ns[total_streams], + context->bw_results + .urgent_wm_ns[total_streams], + stream->public.timing.h_total, + stream->public.timing.pix_clk_khz, + 1000 * dc->bw_vbios.blackout_duration + .value >> 24); + total_streams++; + } + } +} + +static void set_safe_displaymarks(struct validate_context *context) +{ + uint8_t i, j; + uint8_t target_count = context->target_count; + + for (i = 0; i < target_count; i++) { + struct core_target *target = context->targets[i]; + + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + + stream->mi->funcs->mem_input_program_safe_display_marks( + stream->mi); + } + } +} + +static void program_bw(struct dc *dc, struct validate_context *context) +{ + set_safe_displaymarks(context); + /*TODO: when pplib works*/ + /*dc_set_clocks_and_clock_state(context);*/ + + dc->hwss.set_display_clock(context); + dc->hwss.set_displaymarks(dc, context); +} + +static void switch_dp_clock_sources( + const struct dc *dc, + struct validate_context *val_context) +{ + uint8_t i, j; + for (i = 0; i < val_context->target_count; i++) { + struct core_target *target = val_context->targets[i]; + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + + if (dc_is_dp_signal(stream->signal)) { + struct clock_source *clk_src = + find_used_clk_src_for_sharing( + val_context, stream); + + if (clk_src && + clk_src != stream->clock_source) { + unreference_clock_source( + &val_context->res_ctx, + stream->clock_source); + stream->clock_source = clk_src; + reference_clock_source( + &val_context->res_ctx, clk_src); + dc->hwss.crtc_switch_to_clk_src( + clk_src, stream->opp->inst); + } + } + } + } +} + +/******************************************************************************* + * Public functions + ******************************************************************************/ + +/*TODO: const validate_context*/ +static enum dc_status apply_ctx_to_hw( + const struct dc *dc, + struct validate_context *context) +{ + enum dc_status status; + uint8_t i; + struct resource_pool *pool = &context->res_ctx.pool; + + update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv, + true); + + for (i = 0; i < pool->controller_count; i++) { + struct controller_ctx *ctlr_ctx + = &context->res_ctx.controller_ctx[i]; + struct dc_bios *dcb; + + if (ctlr_ctx->flags.unchanged || !ctlr_ctx->stream) + continue; + + dcb = dal_adapter_service_get_bios_parser( + context->res_ctx.pool.adapter_srv); + + dc->hwss.enable_display_power_gating( + dc->ctx, i, dcb, + PIPE_GATING_CONTROL_DISABLE); + } + + set_safe_displaymarks(context); + /*TODO: when pplib works*/ + /*dc_set_clocks_and_clock_state(context);*/ + + if (context->bw_results.dispclk_khz + > dc->current_context.bw_results.dispclk_khz) + set_display_clock(context); + + for (i = 0; i < pool->controller_count; i++) { + struct controller_ctx *ctlr_ctx + = &context->res_ctx.controller_ctx[i]; + if (ctlr_ctx->flags.unchanged || !ctlr_ctx->stream) + continue; + + status = apply_single_controller_ctx_to_hw( + i, + context, + dc); + + if (DC_OK != status) + return status; + } + dc->hwss.set_displaymarks(dc, context); + + update_bios_scratch_critical_state(context->res_ctx.pool.adapter_srv, + false); + + switch_dp_clock_sources(dc, context); + + return DC_OK; +} + + +/******************************************************************************* + * Front End programming + ******************************************************************************/ + +static bool setup_line_buffer_pixel_depth( + const struct core_stream *stream, + enum lb_pixel_depth depth, + bool blank) +{ + enum lb_pixel_depth current_depth; + + struct timing_generator *tg = stream->tg; + struct transform *xfm = stream->xfm; + + if (!xfm->funcs->transform_get_current_pixel_storage_depth( + xfm, + ¤t_depth)) + return false; + + if (current_depth != depth) { + if (blank) + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + + return xfm->funcs->transform_set_pixel_storage_depth(xfm, depth, + &stream->bit_depth_params); + } + + return false; +} + +static void hw_sequencer_build_scaler_parameter_plane( + const struct core_stream *stream, + struct scaler_data *scaler_data) +{ + /*TODO: per pipe not per stream*/ + /*TODO: get from feature from adapterservice*/ + scaler_data->flags.bits.SHOW_COLOURED_BORDER = false; + + scaler_data->flags.bits.SHOULD_PROGRAM_ALPHA = 1; + + scaler_data->flags.bits.SHOULD_PROGRAM_VIEWPORT = 0; + + scaler_data->flags.bits.SHOULD_UNLOCK = 0; + + scaler_data->flags.bits.INTERLACED = 0; + + scaler_data->dal_pixel_format = stream->format; + + scaler_data->taps = stream->taps; + + scaler_data->viewport = stream->viewport; + + scaler_data->overscan = stream->overscan; + + scaler_data->ratios = &stream->ratios; + + /*TODO rotation and adjustment */ + scaler_data->h_sharpness = 0; + scaler_data->v_sharpness = 0; + +} + +static void set_default_colors( + struct input_pixel_processor *ipp, + struct output_pixel_processor *opp, + enum pixel_format format, + enum color_space input_color_space, + enum color_space output_color_space, + enum dc_color_depth color_depth) +{ + struct default_adjustment default_adjust = { 0 }; + + default_adjust.force_hw_default = false; + default_adjust.color_space = output_color_space; + default_adjust.csc_adjust_type = GRAPHICS_CSC_ADJUST_TYPE_SW; + default_adjust.surface_pixel_format = format; + + /* display color depth */ + default_adjust.color_depth = color_depth; + + /* Lb color depth */ + default_adjust.lb_color_depth = LB_PIXEL_DEPTH_24BPP; + /*dal_hw_sequencer_translate_to_lb_color_depth( + build_params-> + line_buffer_params[path_id][plane_id].depth);*/ + + opp->funcs->opp_set_csc_default(opp, &default_adjust); +} + +static void program_scaler( + uint8_t controller_idx, + struct timing_generator *tg, + struct transform *xfm, + const struct core_surface *surface, + const struct core_stream *stream) +{ + struct scaler_data scaler_data = { { 0 } }; + + hw_sequencer_build_scaler_parameter_plane( + stream, + &scaler_data); + + setup_line_buffer_pixel_depth( + stream, + LB_PIXEL_DEPTH_24BPP, + false); + + tg->funcs->set_overscan_blank_color(tg, surface->public.colorimetry.color_space); + + xfm->funcs->transform_set_scaler(xfm, &scaler_data); + + xfm->funcs->transform_update_viewport( + xfm, + &scaler_data.viewport, + false); +} + +/** + * Program the Front End of the Pipe. + * The Back End was already programmed by Set Mode. + */ +static bool set_plane_config( + const struct dc *dc, + struct core_surface *surface, + struct core_target *target) +{ + const struct core_stream *core_stream = + DC_STREAM_TO_CORE(target->public.streams[0]); + const struct dc_crtc_timing *dc_crtc_timing = + &target->public.streams[0]->timing; + struct mem_input *mi = core_stream->mi; + struct input_pixel_processor *ipp = core_stream->ipp; + struct timing_generator *tg = core_stream->tg; + struct transform *xfm = core_stream->xfm; + struct output_pixel_processor *opp = core_stream->opp; + struct dc_context *ctx = core_stream->ctx; + uint8_t controller_idx = core_stream->controller_idx; + + /* TODO: Clean up change, possibly change to use same type */ + enum color_space input_color_space = + surface_color_to_color_space(&(surface->public.colorimetry)); + + dc->hwss.pipe_control_lock( + ctx, + controller_idx, + PIPE_LOCK_CONTROL_MODE, + false); + + /* While a non-root controller is programmed we + * have to lock the root controller. */ + dc->hwss.pipe_control_lock( + ctx, + controller_idx, + PIPE_LOCK_CONTROL_GRAPHICS | + PIPE_LOCK_CONTROL_SCL | + PIPE_LOCK_CONTROL_BLENDER | + PIPE_LOCK_CONTROL_SURFACE, + true); + + tg->funcs->program_timing(tg, dc_crtc_timing, false); + + dc->hwss.enable_fe_clock(ctx, controller_idx, true); + + set_default_colors( + ipp, + opp, + core_stream->format, + input_color_space, + get_output_color_space(dc_crtc_timing), + dc_crtc_timing->display_color_depth); + + /* program Scaler */ + program_scaler( + controller_idx, tg, xfm, surface, core_stream); + + dc->hwss.set_blender_mode( + ctx, + controller_idx, + BLENDER_MODE_CURRENT_PIPE); + + mi->funcs->mem_input_program_surface_config( + mi, + surface->public.format, + &surface->public.tiling_info, + &surface->public.plane_size, + surface->public.rotation); + + dc->hwss.pipe_control_lock( + ctx, + controller_idx, + PIPE_LOCK_CONTROL_GRAPHICS | + PIPE_LOCK_CONTROL_SCL | + PIPE_LOCK_CONTROL_BLENDER | + PIPE_LOCK_CONTROL_SURFACE, + false); + + return true; +} + +static bool update_plane_address( + const struct dc *dc, + const struct core_surface *surface, + struct core_target *target) +{ + const struct core_stream *core_stream = + DC_STREAM_TO_CORE(target->public.streams[0]); + struct dc_context *ctx = core_stream->ctx; + struct mem_input *mi = core_stream->mi; + uint8_t controller_id = core_stream->controller_idx; + + /* TODO: crtc should be per surface, NOT per-target */ + dc->hwss.pipe_control_lock( + ctx, + controller_id, + PIPE_LOCK_CONTROL_SURFACE, + true); + + if (false == + core_stream->mi->funcs->mem_input_program_surface_flip_and_addr( + mi, &surface->public.address, surface->public.flip_immediate)) + return false; + + dc->hwss.pipe_control_lock( + ctx, + controller_id, + PIPE_LOCK_CONTROL_SURFACE, + false); + + return true; +} + +static void reset_single_stream_hw_ctx( + const struct dc *dc, + struct core_stream *stream, + struct validate_context *context) +{ + struct dc_bios *dcb; + + dcb = dal_adapter_service_get_bios_parser( + context->res_ctx.pool.adapter_srv); + if (stream->audio) { + dal_audio_disable_output(stream->audio, + stream->stream_enc->id, + stream->signal); + stream->audio = NULL; + } + + core_link_disable_stream(stream->sink->link, stream); + + stream->tg->funcs->set_blank(stream->tg, true); + stream->tg->funcs->disable_crtc(stream->tg); + stream->mi->funcs->mem_input_deallocate_dmif_buffer( + stream->mi, context->target_count); + stream->xfm->funcs->transform_set_scaler_bypass(stream->xfm); + unreference_clock_source(&context->res_ctx, stream->clock_source); + dc->hwss.enable_display_power_gating( + stream->ctx, stream->controller_idx, dcb, + PIPE_GATING_CONTROL_ENABLE); +} + +static void reset_hw_ctx(struct dc *dc, + struct validate_context *context, + uint8_t target_count) +{ + uint8_t i; + /* look up the targets that have been removed since last commit */ + for (i = 0; i < dc->current_context.target_count; i++) { + const struct core_target *core_target = + dc->current_context.targets[i]; + struct core_stream *core_stream = + DC_STREAM_TO_CORE(core_target->public.streams[0]); + uint8_t controller_idx = core_stream->controller_idx; + + if (context->res_ctx.controller_ctx[controller_idx].stream && + !context->res_ctx.controller_ctx[controller_idx] + .flags.timing_changed) + continue; + + reset_single_stream_hw_ctx(dc, core_stream, &dc->current_context); + } +} + +static void power_down(struct dc *dc) +{ + power_down_all_hw_blocks(dc); + disable_vga_and_power_gate_all_controllers(dc); + +} + +static bool wait_for_reset_trigger_to_occur( + struct dc_context *dc_ctx, + struct timing_generator *tg) +{ + bool rc = false; + + /* To avoid endless loop we wait at most + * frames_to_wait_on_triggered_reset frames for the reset to occur. */ + const uint32_t frames_to_wait_on_triggered_reset = 10; + uint32_t i; + + for (i = 0; i < frames_to_wait_on_triggered_reset; i++) { + + if (!tg->funcs->is_counter_moving(tg)) { + DC_ERROR("TG counter is not moving!\n"); + break; + } + + if (tg->funcs->did_triggered_reset_occur(tg)) { + rc = true; + /* usually occurs at i=1 */ + DC_SYNC_INFO("GSL: reset occurred at wait count: %d\n", + i); + break; + } + + /* Wait for one frame. */ + tg->funcs->wait_for_state(tg, CRTC_STATE_VACTIVE); + tg->funcs->wait_for_state(tg, CRTC_STATE_VBLANK); + } + + if (false == rc) + DC_ERROR("GSL: Timeout on reset trigger!\n"); + + return rc; +} + +/* Enable timing synchronization for a group of Timing Generators. */ +static void enable_timing_synchronization( + struct dc_context *dc_ctx, + uint32_t timing_generator_num, + struct timing_generator *tgs[]) +{ + struct dcp_gsl_params gsl_params = { 0 }; + struct trigger_params trigger_params; + uint32_t i; + + DC_SYNC_INFO("GSL: Setting-up...\n"); + + gsl_params.gsl_group = SYNC_SOURCE_GSL_GROUP0; + gsl_params.gsl_purpose = DCP_GSL_PURPOSE_SURFACE_FLIP; + + for (i = 0; i < timing_generator_num; i++) { + /* Designate a single TG in the group as a master. + * Since HW doesn't care which one, we always assign + * the 1st one in the group. */ + gsl_params.timing_server = (0 == i ? true : false); + + tgs[i]->funcs->setup_global_swap_lock(tgs[i], &gsl_params); + } + + /* Reset slave controllers on master VSync */ + DC_SYNC_INFO("GSL: enabling trigger-reset\n"); + dm_memset(&trigger_params, 0, sizeof(trigger_params)); + + trigger_params.edge = TRIGGER_EDGE_DEFAULT; + trigger_params.source = SYNC_SOURCE_GSL_GROUP0; + + for (i = 1 /* skip the master */; i < timing_generator_num; i++) { + tgs[i]->funcs->enable_reset_trigger(tgs[i], &trigger_params); + + DC_SYNC_INFO("GSL: waiting for reset to occur.\n"); + wait_for_reset_trigger_to_occur(dc_ctx, tgs[i]); + + /* Regardless of success of the wait above, remove the reset or + * the driver will start timing out on Display requests. */ + DC_SYNC_INFO("GSL: disabling trigger-reset.\n"); + tgs[i]->funcs->disable_reset_trigger(tgs[i]); + } + + /* GSL Vblank synchronization is a one time sync mechanism, assumption + * is that the sync'ed displays will not drift out of sync over time*/ + DC_SYNC_INFO("GSL: Restoring register states.\n"); + for (i = 0; i < timing_generator_num; i++) + tgs[i]->funcs->tear_down_global_swap_lock(tgs[i]); + + DC_SYNC_INFO("GSL: Set-up complete.\n"); +} + +static const struct hw_sequencer_funcs dce110_funcs = { + .apply_ctx_to_hw = apply_ctx_to_hw, + .reset_hw_ctx = reset_hw_ctx, + .set_plane_config = set_plane_config, + .update_plane_address = update_plane_address, + .set_gamma_ramp = set_gamma_ramp, + .power_down = power_down, + .enable_accelerated_mode = enable_accelerated_mode, + .enable_timing_synchronization = enable_timing_synchronization, + .program_bw = program_bw, + .enable_stream = enable_stream, + .disable_stream = disable_stream, + .enable_display_pipe_clock_gating = enable_display_pipe_clock_gating, + .crtc_switch_to_clk_src = dce110_crtc_switch_to_clk_src, + .enable_display_power_gating = dce110_enable_display_power_gating, + .enable_fe_clock = dce110_enable_fe_clock, + .pipe_control_lock = dce110_pipe_control_lock, + .set_blender_mode = dce110_set_blender_mode, + .clock_gating_power_up = dal_dc_clock_gating_dce110_power_up,/*todo*/ + .set_display_clock = set_display_clock, + .set_displaymarks = set_displaymarks, +}; + +bool dce110_hw_sequencer_construct(struct dc *dc) +{ + dc->hwss = dce110_funcs; + + return true; +} + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h new file mode 100644 index 000000000000..def54df283d5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_hw_sequencer.h @@ -0,0 +1,36 @@ +/* +* Copyright 2012-15 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 + * + */ + +#ifndef __DC_HWSS_DCE110_H__ +#define __DC_HWSS_DCE110_H__ + +#include "core_types.h" + +struct dc; + +bool dce110_hw_sequencer_construct(struct dc *dc); + +#endif /* __DC_HWSS_DCE110_H__ */ + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c new file mode 100644 index 000000000000..9e2b5d95a521 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.c @@ -0,0 +1,1238 @@ +/* +* Copyright 2012-15 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 "link_encoder.h" +#include "stream_encoder.h" + +#include "resource.h" +#include "include/irq_service_interface.h" +#include "../virtual/virtual_stream_encoder.h" +#include "dce110/dce110_timing_generator.h" +#include "dce110/dce110_link_encoder.h" +#include "dce110/dce110_mem_input.h" +#include "dce110/dce110_ipp.h" +#include "dce110/dce110_transform.h" +#include "dce110/dce110_stream_encoder.h" +#include "dce110/dce110_opp.h" +#include "dce110/dce110_clock_source.h" + +#include "dce/dce_11_0_d.h" + +#ifndef mmDP_DPHY_INTERNAL_CTRL + #define mmDP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP0_DP_DPHY_INTERNAL_CTRL 0x4aa7 + #define mmDP1_DP_DPHY_INTERNAL_CTRL 0x4ba7 + #define mmDP2_DP_DPHY_INTERNAL_CTRL 0x4ca7 + #define mmDP3_DP_DPHY_INTERNAL_CTRL 0x4da7 + #define mmDP4_DP_DPHY_INTERNAL_CTRL 0x4ea7 + #define mmDP5_DP_DPHY_INTERNAL_CTRL 0x4fa7 + #define mmDP6_DP_DPHY_INTERNAL_CTRL 0x54a7 + #define mmDP7_DP_DPHY_INTERNAL_CTRL 0x56a7 + #define mmDP8_DP_DPHY_INTERNAL_CTRL 0x57a7 +#endif + +enum dce110_clk_src_array_id { + DCE110_CLK_SRC_PLL0 = 0, + DCE110_CLK_SRC_PLL1, + DCE110_CLK_SRC_EXT, + + DCE110_CLK_SRC_TOTAL +}; + +static const struct dce110_timing_generator_offsets dce110_tg_offsets[] = { + { + .crtc = (mmCRTC0_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC1_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC2_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC3_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP3_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC4_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP4_GRPH_CONTROL - mmGRPH_CONTROL), + }, + { + .crtc = (mmCRTC5_CRTC_CONTROL - mmCRTC_CONTROL), + .dcp = (mmDCP5_GRPH_CONTROL - mmGRPH_CONTROL), + } +}; + +static const struct dce110_mem_input_reg_offsets dce110_mi_reg_offsets[] = { + { + .dcp = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG0_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE0_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG1_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE1_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + }, + { + .dcp = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .dmif = (mmDMIF_PG2_DPG_WATERMARK_MASK_CONTROL + - mmDPG_WATERMARK_MASK_CONTROL), + .pipe = (mmPIPE2_DMIF_BUFFER_CONTROL + - mmPIPE0_DMIF_BUFFER_CONTROL), + } +}; + +static const struct dce110_transform_reg_offsets dce110_xfm_offsets[] = { +{ + .scl_offset = (mmSCL0_SCL_CONTROL - mmSCL_CONTROL), + .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB0_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ .scl_offset = (mmSCL1_SCL_CONTROL - mmSCL_CONTROL), + .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB1_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +}, +{ .scl_offset = (mmSCL2_SCL_CONTROL - mmSCL_CONTROL), + .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmGRPH_CONTROL), + .lb_offset = (mmLB2_LB_DATA_FORMAT - mmLB_DATA_FORMAT), +} +}; + +static const struct dce110_ipp_reg_offsets dce110_ipp_reg_offsets[] = { +{ + .dcp_offset = (mmDCP0_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP1_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP2_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP3_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP4_CUR_CONTROL - mmCUR_CONTROL), +}, +{ + .dcp_offset = (mmDCP5_CUR_CONTROL - mmCUR_CONTROL), +} +}; + +static const struct dce110_link_enc_bl_registers link_enc_bl_regs = { + .BL_PWM_CNTL = mmBL_PWM_CNTL, + .BL_PWM_GRP1_REG_LOCK = mmBL_PWM_GRP1_REG_LOCK, + .BL_PWM_PERIOD_CNTL = mmBL_PWM_PERIOD_CNTL, + .LVTMA_PWRSEQ_CNTL = mmLVTMA_PWRSEQ_CNTL, + .LVTMA_PWRSEQ_STATE = mmLVTMA_PWRSEQ_STATE +}; + +#define aux_regs(id)\ +[id] = {\ + .AUX_CONTROL = mmDP_AUX ## id ## _AUX_CONTROL,\ + .AUX_DPHY_RX_CONTROL0 = mmDP_AUX ## id ## _AUX_DPHY_RX_CONTROL0\ +} + +static const struct dce110_link_enc_aux_registers link_enc_aux_regs[] = { + aux_regs(0), + aux_regs(1), + aux_regs(2), + aux_regs(3), + aux_regs(4), + aux_regs(5) +}; + +#define link_regs(id)\ +[id] = {\ + .DIG_BE_CNTL = mmDIG ## id ## _DIG_BE_CNTL,\ + .DIG_BE_EN_CNTL = mmDIG ## id ## _DIG_BE_EN_CNTL,\ + .DP_CONFIG = mmDP ## id ## _DP_CONFIG,\ + .DP_DPHY_CNTL = mmDP ## id ## _DP_DPHY_CNTL,\ + .DP_DPHY_INTERNAL_CTRL = mmDP ## id ## _DP_DPHY_INTERNAL_CTRL,\ + .DP_DPHY_PRBS_CNTL = mmDP ## id ## _DP_DPHY_PRBS_CNTL,\ + .DP_DPHY_SYM0 = mmDP ## id ## _DP_DPHY_SYM0,\ + .DP_DPHY_SYM1 = mmDP ## id ## _DP_DPHY_SYM1,\ + .DP_DPHY_SYM2 = mmDP ## id ## _DP_DPHY_SYM2,\ + .DP_DPHY_TRAINING_PATTERN_SEL = mmDP ## id ## _DP_DPHY_TRAINING_PATTERN_SEL,\ + .DP_LINK_CNTL = mmDP ## id ## _DP_LINK_CNTL,\ + .DP_LINK_FRAMING_CNTL = mmDP ## id ## _DP_LINK_FRAMING_CNTL,\ + .DP_MSE_SAT0 = mmDP ## id ## _DP_MSE_SAT0,\ + .DP_MSE_SAT1 = mmDP ## id ## _DP_MSE_SAT1,\ + .DP_MSE_SAT2 = mmDP ## id ## _DP_MSE_SAT2,\ + .DP_MSE_SAT_UPDATE = mmDP ## id ## _DP_MSE_SAT_UPDATE,\ + .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\ + .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL\ +} + +static const struct dce110_link_enc_registers link_enc_regs[] = { + link_regs(0), + link_regs(1), + link_regs(2), + link_regs(3), + link_regs(4), + link_regs(5), + link_regs(6) +}; + +#define stream_enc_regs(id)\ +[id] = {\ + .AFMT_AVI_INFO0 = mmDIG ## id ## _AFMT_AVI_INFO0,\ + .AFMT_AVI_INFO1 = mmDIG ## id ## _AFMT_AVI_INFO1,\ + .AFMT_AVI_INFO2 = mmDIG ## id ## _AFMT_AVI_INFO2,\ + .AFMT_AVI_INFO3 = mmDIG ## id ## _AFMT_AVI_INFO3,\ + .AFMT_GENERIC_0 = mmDIG ## id ## _AFMT_GENERIC_0,\ + .AFMT_GENERIC_7 = mmDIG ## id ## _AFMT_GENERIC_7,\ + .AFMT_GENERIC_HDR = mmDIG ## id ## _AFMT_GENERIC_HDR,\ + .AFMT_INFOFRAME_CONTROL0 = mmDIG ## id ## _AFMT_INFOFRAME_CONTROL0,\ + .AFMT_VBI_PACKET_CONTROL = mmDIG ## id ## _AFMT_VBI_PACKET_CONTROL,\ + .DIG_FE_CNTL = mmDIG ## id ## _DIG_FE_CNTL,\ + .DP_MSE_RATE_CNTL = mmDP ## id ## _DP_MSE_RATE_CNTL,\ + .DP_MSE_RATE_UPDATE = mmDP ## id ## _DP_MSE_RATE_UPDATE,\ + .DP_PIXEL_FORMAT = mmDP ## id ## _DP_PIXEL_FORMAT,\ + .DP_SEC_CNTL = mmDP ## id ## _DP_SEC_CNTL,\ + .DP_STEER_FIFO = mmDP ## id ## _DP_STEER_FIFO,\ + .DP_VID_M = mmDP ## id ## _DP_VID_M,\ + .DP_VID_N = mmDP ## id ## _DP_VID_N,\ + .DP_VID_STREAM_CNTL = mmDP ## id ## _DP_VID_STREAM_CNTL,\ + .DP_VID_TIMING = mmDP ## id ## _DP_VID_TIMING,\ + .HDMI_CONTROL = mmDIG ## id ## _HDMI_CONTROL,\ + .HDMI_GC = mmDIG ## id ## _HDMI_GC,\ + .HDMI_GENERIC_PACKET_CONTROL0 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL0,\ + .HDMI_GENERIC_PACKET_CONTROL1 = mmDIG ## id ## _HDMI_GENERIC_PACKET_CONTROL1,\ + .HDMI_INFOFRAME_CONTROL0 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL0,\ + .HDMI_INFOFRAME_CONTROL1 = mmDIG ## id ## _HDMI_INFOFRAME_CONTROL1,\ + .HDMI_VBI_PACKET_CONTROL = mmDIG ## id ## _HDMI_VBI_PACKET_CONTROL,\ + .TMDS_CNTL = mmDIG ## id ## _TMDS_CNTL\ +} + +static const struct dce110_stream_enc_registers stream_enc_regs[] = { + stream_enc_regs(0), + stream_enc_regs(1), + stream_enc_regs(2), + stream_enc_regs(3), + stream_enc_regs(4), + stream_enc_regs(5), + stream_enc_regs(6) +}; + + +/* AG TBD Needs to be reduced back to 3 pipes once dce10 hw sequencer implemented. */ +static const struct dce110_opp_reg_offsets dce110_opp_reg_offsets[] = { +{ + .fmt_offset = (mmFMT0_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE0_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT1_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE1_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT2_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE2_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ + .fmt_offset = (mmFMT3_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE3_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT4_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE4_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +}, +{ .fmt_offset = (mmFMT5_FMT_CONTROL - mmFMT0_FMT_CONTROL), + .dcfe_offset = (mmDCFE5_DCFE_MEM_PWR_CTRL - mmDCFE0_DCFE_MEM_PWR_CTRL), + .dcp_offset = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), +} +}; + + +static const struct dce110_clk_src_reg_offsets dce110_clk_src_reg_offsets[] = { + { + .pll_cntl = mmBPHYC_PLL0_PLL_CNTL, + .pixclk_resync_cntl = mmPIXCLK0_RESYNC_CNTL + }, + { + .pll_cntl = mmBPHYC_PLL1_PLL_CNTL, + .pixclk_resync_cntl = mmPIXCLK1_RESYNC_CNTL + } +}; + +static struct timing_generator *dce110_timing_generator_create( + struct adapter_service *as, + struct dc_context *ctx, + uint32_t instance, + const struct dce110_timing_generator_offsets *offsets) +{ + struct dce110_timing_generator *tg110 = + dm_alloc(ctx, sizeof(struct dce110_timing_generator)); + + if (!tg110) + return NULL; + + if (dce110_timing_generator_construct(tg110, as, ctx, instance, offsets)) + return &tg110->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, tg110); + return NULL; +} + +static struct stream_encoder *dce110_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx, + struct dc_bios *bp, + const struct dce110_stream_enc_registers *regs) +{ + struct dce110_stream_encoder *enc110 = + dm_alloc(ctx, sizeof(struct dce110_stream_encoder)); + + if (!enc110) + return NULL; + + if (dce110_stream_encoder_construct(enc110, ctx, bp, eng_id, regs)) + return &enc110->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, enc110); + return NULL; +} + +static struct mem_input *dce110_mem_input_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_mem_input_reg_offsets *offset) +{ + struct dce110_mem_input *mem_input110 = + dm_alloc(ctx, sizeof(struct dce110_mem_input)); + + if (!mem_input110) + return NULL; + + if (dce110_mem_input_construct(mem_input110, + ctx, inst, offset)) + return &mem_input110->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, mem_input110); + return NULL; +} + +static void dce110_transform_destroy(struct transform **xfm) +{ + dm_free((*xfm)->ctx, TO_DCE110_TRANSFORM(*xfm)); + *xfm = NULL; +} + +static struct transform *dce110_transform_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_transform_reg_offsets *offsets) +{ + struct dce110_transform *transform = + dm_alloc(ctx, sizeof(struct dce110_transform)); + + if (!transform) + return NULL; + + if (dce110_transform_construct(transform, ctx, inst, offsets)) + return &transform->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, transform); + return NULL; +} + +static struct input_pixel_processor *dce110_ipp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_ipp_reg_offsets *offsets) +{ + struct dce110_ipp *ipp = + dm_alloc(ctx, sizeof(struct dce110_ipp)); + + if (!ipp) + return NULL; + + if (dce110_ipp_construct(ipp, ctx, inst, offsets)) + return &ipp->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, ipp); + return NULL; +} + +struct link_encoder *dce110_link_encoder_create( + const struct encoder_init_data *enc_init_data) +{ + struct dce110_link_encoder *enc110 = + dm_alloc( + enc_init_data->ctx, + sizeof(struct dce110_link_encoder)); + + if (!enc110) + return NULL; + + if (dce110_link_encoder_construct( + enc110, + enc_init_data, + &link_enc_regs[enc_init_data->transmitter], + &link_enc_aux_regs[enc_init_data->channel - 1], + &link_enc_bl_regs)) + return &enc110->base; + + BREAK_TO_DEBUGGER(); + dm_free(enc_init_data->ctx, enc110); + return NULL; +} + +void dce110_link_encoder_destroy(struct link_encoder **enc) +{ + dm_free((*enc)->ctx, TO_DCE110_LINK_ENC(*enc)); + *enc = NULL; +} + + +static struct output_pixel_processor *dce110_opp_create( + struct dc_context *ctx, + uint32_t inst, + const struct dce110_opp_reg_offsets *offsets) +{ + struct dce110_opp *opp = + dm_alloc(ctx, sizeof(struct dce110_opp)); + + if (!opp) + return NULL; + + if (dce110_opp_construct(opp, + ctx, inst, offsets)) + return &opp->base; + + BREAK_TO_DEBUGGER(); + dm_free(ctx, opp); + return NULL; +} + +struct clock_source *dce110_clock_source_create( + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_reg_offsets *offsets) +{ + struct dce110_clk_src *clk_src = + dm_alloc(ctx, sizeof(struct dce110_clk_src)); + + if (!clk_src) + return NULL; + + if (dce110_clk_src_construct(clk_src, ctx, bios, id, offsets)) + return &clk_src->base; + + BREAK_TO_DEBUGGER(); + return NULL; +} + +void dce110_clock_source_destroy(struct clock_source **clk_src) +{ + dm_free((*clk_src)->ctx, TO_DCE110_CLK_SRC(*clk_src)); + *clk_src = NULL; +} + +void dce110_destruct_resource_pool(struct resource_pool *pool) +{ + unsigned int i; + + for (i = 0; i < pool->controller_count; i++) { + if (pool->opps[i] != NULL) + dce110_opp_destroy(&pool->opps[i]); + + if (pool->transforms[i] != NULL) + dce110_transform_destroy(&pool->transforms[i]); + + if (pool->ipps[i] != NULL) + dce110_ipp_destroy(&pool->ipps[i]); + + if (pool->mis[i] != NULL) { + dm_free(pool->mis[i]->ctx, + TO_DCE110_MEM_INPUT(pool->mis[i])); + pool->mis[i] = NULL; + } + + if (pool->timing_generators[i] != NULL) { + dm_free(pool->timing_generators[i]->ctx, DCE110TG_FROM_TG(pool->timing_generators[i])); + pool->timing_generators[i] = NULL; + } + } + + for (i = 0; i < pool->stream_enc_count; i++) { + if (pool->stream_enc[i] != NULL) + dm_free(pool->stream_enc[i]->ctx, + DCE110STRENC_FROM_STRENC(pool->stream_enc[i])); + } + + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] != NULL) { + dce110_clock_source_destroy(&pool->clock_sources[i]); + } + } + + for (i = 0; i < pool->audio_count; i++) { + if (pool->audios[i] != NULL) { + dal_audio_destroy(&pool->audios[i]); + } + } + + if (pool->display_clock != NULL) { + dal_display_clock_destroy(&pool->display_clock); + } + + if (pool->scaler_filter != NULL) { + dal_scaler_filter_destroy(&pool->scaler_filter); + } + if (pool->irqs != NULL) { + dal_irq_service_destroy(&pool->irqs); + } + + if (pool->adapter_srv != NULL) { + dal_adapter_service_destroy(&pool->adapter_srv); + } +} + +static struct clock_source *find_first_free_pll( + struct resource_context *res_ctx) +{ + if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL0] == 0) { + return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL0]; + } + if (res_ctx->clock_source_ref_count[DCE110_CLK_SRC_PLL1] == 0) { + return res_ctx->pool.clock_sources[DCE110_CLK_SRC_PLL1]; + } + + return 0; +} + +static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) +{ + switch (crtc_id) { + case CONTROLLER_ID_D0: + return DTO_SOURCE_ID0; + case CONTROLLER_ID_D1: + return DTO_SOURCE_ID1; + case CONTROLLER_ID_D2: + return DTO_SOURCE_ID2; + case CONTROLLER_ID_D3: + return DTO_SOURCE_ID3; + case CONTROLLER_ID_D4: + return DTO_SOURCE_ID4; + case CONTROLLER_ID_D5: + return DTO_SOURCE_ID5; + default: + return DTO_SOURCE_UNKNOWN; + } +} + +static void build_audio_output( + const struct core_stream *stream, + struct audio_output *audio_output) +{ + audio_output->engine_id = stream->stream_enc->id; + + audio_output->signal = stream->signal; + + /* audio_crtc_info */ + + audio_output->crtc_info.h_total = + stream->public.timing.h_total; + + /* Audio packets are sent during actual CRTC blank physical signal, we + * need to specify actual active signal portion */ + audio_output->crtc_info.h_active = + stream->public.timing.h_addressable + + stream->public.timing.h_border_left + + stream->public.timing.h_border_right; + + audio_output->crtc_info.v_active = + stream->public.timing.v_addressable + + stream->public.timing.v_border_top + + stream->public.timing.v_border_bottom; + + audio_output->crtc_info.pixel_repetition = 1; + + audio_output->crtc_info.interlaced = + stream->public.timing.flags.INTERLACE; + + audio_output->crtc_info.refresh_rate = + (stream->public.timing.pix_clk_khz*1000)/ + (stream->public.timing.h_total*stream->public.timing.v_total); + + audio_output->crtc_info.color_depth = + stream->public.timing.display_color_depth; + + audio_output->crtc_info.requested_pixel_clock = + stream->pix_clk_params.requested_pix_clk; + + /* TODO - Investigate why calculated pixel clk has to be + * requested pixel clk */ + audio_output->crtc_info.calculated_pixel_clock = + stream->pix_clk_params.requested_pix_clk; + + if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + audio_output->pll_info.dp_dto_source_clock_in_khz = + dal_display_clock_get_dp_ref_clk_frequency( + stream->dis_clk); + } + + audio_output->pll_info.feed_back_divider = + stream->pll_settings.feedback_divider; + + audio_output->pll_info.dto_source = + translate_to_dto_source( + stream->controller_idx + 1); + + /* TODO hard code to enable for now. Need get from stream */ + audio_output->pll_info.ss_enabled = true; + + audio_output->pll_info.ss_percentage = + stream->pll_settings.ss_percentage; +} + +static void get_pixel_clock_parameters( + const struct core_stream *stream, + struct pixel_clk_params *pixel_clk_params) +{ + pixel_clk_params->requested_pix_clk = stream->public.timing.pix_clk_khz; + pixel_clk_params->encoder_object_id = stream->sink->link->link_enc->id; + pixel_clk_params->signal_type = stream->sink->public.sink_signal; + pixel_clk_params->controller_id = stream->controller_idx + 1; + /* TODO: un-hardcode*/ + pixel_clk_params->requested_sym_clk = LINK_RATE_LOW * + LINK_RATE_REF_FREQ_IN_KHZ; + pixel_clk_params->flags.ENABLE_SS = 0; + pixel_clk_params->color_depth = + stream->public.timing.display_color_depth; + pixel_clk_params->flags.DISPLAY_BLANKED = 1; +} + +static enum dc_status build_stream_hw_param(struct core_stream *stream) +{ + /*TODO: unhardcode*/ + stream->max_tmds_clk_from_edid_in_mhz = 0; + stream->max_hdmi_deep_color = COLOR_DEPTH_121212; + stream->max_hdmi_pixel_clock = 600000; + + get_pixel_clock_parameters(stream, &stream->pix_clk_params); + stream->clock_source->funcs->get_pix_clk_dividers( + stream->clock_source, + &stream->pix_clk_params, + &stream->pll_settings); + + build_audio_output(stream, &stream->audio_output); + + return DC_OK; +} + +static enum dc_status validate_mapped_resource( + const struct dc *dc, + struct validate_context *context) +{ + enum dc_status status = DC_OK; + uint8_t i, j; + + for (i = 0; i < context->target_count; i++) { + struct core_target *target = context->targets[i]; + if (context->target_flags[i].unchanged) + continue; + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + struct core_link *link = stream->sink->link; + + if (!stream->tg->funcs->validate_timing( + stream->tg, &stream->public.timing)) + return DC_FAIL_CONTROLLER_VALIDATE; + + status = build_stream_hw_param(stream); + + if (status != DC_OK) + return status; + + if (!link->link_enc->funcs->validate_output_with_stream( + link->link_enc, + stream)) + return DC_FAIL_ENC_VALIDATE; + + /* TODO: validate audio ASIC caps, encoder */ + + status = dc_link_validate_mode_timing(stream->sink, + link, + &stream->public.timing); + + if (status != DC_OK) + return status; + + build_info_frame(stream); + } + } + + return DC_OK; +} + +enum dc_status dce110_validate_bandwidth( + const struct dc *dc, + struct validate_context *context) +{ + uint8_t i, j; + enum dc_status result = DC_ERROR_UNEXPECTED; + uint8_t number_of_displays = 0; + uint8_t max_htaps = 1; + uint8_t max_vtaps = 1; + bool all_displays_in_sync = true; + struct dc_crtc_timing prev_timing; + + memset(&context->bw_mode_data, 0, sizeof(context->bw_mode_data)); + + for (i = 0; i < context->target_count; i++) { + struct core_target *target = context->targets[i]; + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + struct bw_calcs_input_single_display *disp = &context-> + bw_mode_data.displays_data[number_of_displays]; + + if (target->status.surface_count == 0) { + disp->graphics_scale_ratio = bw_int_to_fixed(1); + disp->graphics_h_taps = 2; + disp->graphics_v_taps = 2; + + /* TODO: remove when bw formula accepts taps per + * display + */ + if (max_vtaps < 2) + max_vtaps = 2; + if (max_htaps < 2) + max_htaps = 2; + + } else { + disp->graphics_scale_ratio = + fixed31_32_to_bw_fixed( + stream->ratios.vert.value); + disp->graphics_h_taps = stream->taps.h_taps; + disp->graphics_v_taps = stream->taps.v_taps; + + /* TODO: remove when bw formula accepts taps per + * display + */ + if (max_vtaps < stream->taps.v_taps) + max_vtaps = stream->taps.v_taps; + if (max_htaps < stream->taps.h_taps) + max_htaps = stream->taps.h_taps; + } + + disp->graphics_src_width = + stream->public.timing.h_addressable; + disp->graphics_src_height = + stream->public.timing.v_addressable; + disp->h_total = stream->public.timing.h_total; + disp->pixel_rate = bw_frc_to_fixed( + stream->public.timing.pix_clk_khz, 1000); + + /*TODO: get from surface*/ + disp->graphics_bytes_per_pixel = 4; + disp->graphics_tiling_mode = bw_def_tiled; + + /* DCE11 defaults*/ + disp->graphics_lb_bpc = 10; + disp->graphics_interlace_mode = false; + disp->fbc_enable = false; + disp->lpt_enable = false; + disp->graphics_stereo_mode = bw_def_mono; + disp->underlay_mode = bw_def_none; + + /*All displays will be synchronized if timings are all + * the same + */ + if (number_of_displays != 0 && all_displays_in_sync) + if (dm_memcmp(&prev_timing, + &stream->public.timing, + sizeof(struct dc_crtc_timing))!= 0) + all_displays_in_sync = false; + if (number_of_displays == 0) + prev_timing = stream->public.timing; + + number_of_displays++; + } + } + + /* TODO: remove when bw formula accepts taps per + * display + */ + context->bw_mode_data.displays_data[0].graphics_v_taps = max_vtaps; + context->bw_mode_data.displays_data[0].graphics_h_taps = max_htaps; + + context->bw_mode_data.number_of_displays = number_of_displays; + context->bw_mode_data.display_synchronization_enabled = + all_displays_in_sync; + + dal_logger_write( + dc->ctx->logger, + LOG_MAJOR_BWM, + LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS, + "%s: start\n", + __func__); + + if (!bw_calcs( + dc->ctx, + &dc->bw_dceip, + &dc->bw_vbios, + &context->bw_mode_data, + &context->bw_results)) + result = DC_FAIL_BANDWIDTH_VALIDATE; + else + result = DC_OK; + + if (result == DC_FAIL_BANDWIDTH_VALIDATE) + dal_logger_write(dc->ctx->logger, + LOG_MAJOR_BWM, + LOG_MINOR_BWM_MODE_VALIDATION, + "%s: Bandwidth validation failed!", + __func__); + + if (dm_memcmp(&dc->current_context.bw_results, + &context->bw_results, sizeof(context->bw_results))) { + struct log_entry log_entry; + dal_logger_open( + dc->ctx->logger, + &log_entry, + LOG_MAJOR_BWM, + LOG_MINOR_BWM_REQUIRED_BANDWIDTH_CALCS); + dal_logger_append(&log_entry, "%s: finish, numDisplays: %d\n" + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + __func__, number_of_displays, + context->bw_results.nbp_state_change_wm_ns[0].b_mark, + context->bw_results.nbp_state_change_wm_ns[0].a_mark, + context->bw_results.urgent_wm_ns[0].b_mark, + context->bw_results.urgent_wm_ns[0].a_mark, + context->bw_results.stutter_exit_wm_ns[0].b_mark, + context->bw_results.stutter_exit_wm_ns[0].a_mark); + dal_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d\n", + context->bw_results.nbp_state_change_wm_ns[1].b_mark, + context->bw_results.nbp_state_change_wm_ns[1].a_mark, + context->bw_results.urgent_wm_ns[1].b_mark, + context->bw_results.urgent_wm_ns[1].a_mark, + context->bw_results.stutter_exit_wm_ns[1].b_mark, + context->bw_results.stutter_exit_wm_ns[1].a_mark); + dal_logger_append(&log_entry, + "nbpMark_b: %d nbpMark_a: %d urgentMark_b: %d urgentMark_a: %d\n" + "stutMark_b: %d stutMark_a: %d stutter_mode_enable: %d\n", + context->bw_results.nbp_state_change_wm_ns[2].b_mark, + context->bw_results.nbp_state_change_wm_ns[2].a_mark, + context->bw_results.urgent_wm_ns[2].b_mark, + context->bw_results.urgent_wm_ns[2].a_mark, + context->bw_results.stutter_exit_wm_ns[2].b_mark, + context->bw_results.stutter_exit_wm_ns[2].a_mark, + context->bw_results.stutter_mode_enable); + dal_logger_append(&log_entry, + "cstate: %d pstate: %d nbpstate: %d sync: %d dispclk: %d\n" + "sclk: %d sclk_sleep: %d yclk: %d blackout_duration: %d\n", + context->bw_results.cpuc_state_change_enable, + context->bw_results.cpup_state_change_enable, + context->bw_results.nbp_state_change_enable, + context->bw_results.all_displays_in_sync, + context->bw_results.dispclk_khz, + context->bw_results.required_sclk, + context->bw_results.required_sclk_deep_sleep, + context->bw_results.required_yclk, + context->bw_results.required_blackout_duration_us); + dal_logger_close(&log_entry); + } + return result; +} + +static void set_target_unchanged( + struct validate_context *context, + uint8_t target_idx) +{ + uint8_t i; + struct core_target *target = context->targets[target_idx]; + context->target_flags[target_idx].unchanged = true; + for (i = 0; i < target->public.stream_count; i++) { + struct core_stream *core_stream = + DC_STREAM_TO_CORE(target->public.streams[i]); + uint8_t index = core_stream->controller_idx; + context->res_ctx.controller_ctx[index].flags.unchanged = true; + } +} + +static enum dc_status map_clock_resources( + const struct dc *dc, + struct validate_context *context) +{ + uint8_t i, j; + + /* mark resources used for targets that are already active */ + for (i = 0; i < context->target_count; i++) { + struct core_target *target = context->targets[i]; + + if (!context->target_flags[i].unchanged) + continue; + + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + + reference_clock_source( + &context->res_ctx, + stream->clock_source); + } + } + + /* acquire new resources */ + for (i = 0; i < context->target_count; i++) { + struct core_target *target = context->targets[i]; + + if (context->target_flags[i].unchanged) + continue; + + for (j = 0; j < target->public.stream_count; j++) { + struct core_stream *stream = + DC_STREAM_TO_CORE(target->public.streams[j]); + + if (dc_is_dp_signal(stream->signal) + || stream->signal == SIGNAL_TYPE_VIRTUAL) + stream->clock_source = context->res_ctx. + pool.clock_sources[DCE110_CLK_SRC_EXT]; + else + stream->clock_source = + find_used_clk_src_for_sharing( + context, stream); + if (stream->clock_source == NULL) + stream->clock_source = + find_first_free_pll(&context->res_ctx); + + if (stream->clock_source == NULL) + return DC_NO_CLOCK_SOURCE_RESOURCE; + + reference_clock_source( + &context->res_ctx, + stream->clock_source); + } + } + + return DC_OK; +} + +enum dc_status dce110_validate_with_context( + const struct dc *dc, + const struct dc_validation_set set[], + uint8_t set_count, + struct validate_context *context) +{ + enum dc_status result = DC_ERROR_UNEXPECTED; + uint8_t i, j; + struct dc_context *dc_ctx = dc->ctx; + + for (i = 0; i < set_count; i++) { + context->targets[i] = DC_TARGET_TO_CORE(set[i].target); + + for (j = 0; j < dc->current_context.target_count; j++) + if (dc->current_context.targets[j] == context->targets[i]) + set_target_unchanged(context, i); + + if (!context->target_flags[i].unchanged) + if (!logical_attach_surfaces_to_target( + (struct dc_surface **)set[i].surfaces, + set[i].surface_count, + &context->targets[i]->public)) { + DC_ERROR("Failed to attach surface to target!\n"); + return DC_FAIL_ATTACH_SURFACES; + } + } + + context->target_count = set_count; + + context->res_ctx.pool = dc->res_pool; + + result = map_resources(dc, context); + + if (result == DC_OK) + result = map_clock_resources(dc, context); + + if (result == DC_OK) + result = validate_mapped_resource(dc, context); + + if (result == DC_OK) + build_scaling_params_for_context(dc, context); + + if (result == DC_OK) + result = dce110_validate_bandwidth(dc, context); + + return result; +} + +static struct resource_funcs dce110_res_pool_funcs = { + .destruct = dce110_destruct_resource_pool, + .link_enc_create = dce110_link_encoder_create, + .link_enc_destroy = dce110_link_encoder_destroy, + .validate_with_context = dce110_validate_with_context, + .validate_bandwidth = dce110_validate_bandwidth +}; + +bool dce110_construct_resource_pool( + struct adapter_service *adapter_serv, + uint8_t num_virtual_links, + struct dc *dc, + struct resource_pool *pool) +{ + unsigned int i; + struct audio_init_data audio_init_data = { 0 }; + struct dc_context *ctx = dc->ctx; + pool->adapter_srv = adapter_serv; + pool->funcs = &dce110_res_pool_funcs; + + pool->stream_engines.engine.ENGINE_ID_DIGA = 1; + pool->stream_engines.engine.ENGINE_ID_DIGB = 1; + pool->stream_engines.engine.ENGINE_ID_DIGC = 1; + pool->stream_engines.engine.ENGINE_ID_DIGD = 1; + pool->stream_engines.engine.ENGINE_ID_DIGE = 1; + pool->stream_engines.engine.ENGINE_ID_DIGF = 1; + + pool->clock_sources[DCE110_CLK_SRC_PLL0] = dce110_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_PLL0, &dce110_clk_src_reg_offsets[0]); + pool->clock_sources[DCE110_CLK_SRC_PLL1] = dce110_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_PLL1, &dce110_clk_src_reg_offsets[1]); + pool->clock_sources[DCE110_CLK_SRC_EXT] = dce110_clock_source_create( + ctx, dal_adapter_service_get_bios_parser(adapter_serv), + CLOCK_SOURCE_ID_EXTERNAL, &dce110_clk_src_reg_offsets[0]); + pool->clk_src_count = DCE110_CLK_SRC_TOTAL; + + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] == NULL) { + dm_error("DC: failed to create clock sources!\n"); + BREAK_TO_DEBUGGER(); + goto clk_src_create_fail; + } + } + + pool->display_clock = dal_display_clock_dce110_create(ctx, adapter_serv); + if (pool->display_clock == NULL) { + dm_error("DC: failed to create display clock!\n"); + BREAK_TO_DEBUGGER(); + goto disp_clk_create_fail; + } + + { + struct irq_service_init_data init_data; + init_data.ctx = dc->ctx; + pool->irqs = dal_irq_service_create( + dal_adapter_service_get_dce_version( + dc->res_pool.adapter_srv), + &init_data); + if (!pool->irqs) + goto irqs_create_fail; + + } + + pool->controller_count = + dal_adapter_service_get_func_controllers_num(adapter_serv); + pool->stream_enc_count = dal_adapter_service_get_stream_engines_num( + adapter_serv); + pool->scaler_filter = dal_scaler_filter_create(ctx); + if (pool->scaler_filter == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create filter!\n"); + goto filter_create_fail; + } + + for (i = 0; i < pool->controller_count; i++) { + pool->timing_generators[i] = dce110_timing_generator_create( + adapter_serv, ctx, i, &dce110_tg_offsets[i]); + if (pool->timing_generators[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create tg!\n"); + goto controller_create_fail; + } + + pool->mis[i] = dce110_mem_input_create(ctx, i, + &dce110_mi_reg_offsets[i]); + if (pool->mis[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create memory input!\n"); + goto controller_create_fail; + } + + pool->ipps[i] = dce110_ipp_create(ctx, i, &dce110_ipp_reg_offsets[i]); + if (pool->ipps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create input pixel processor!\n"); + goto controller_create_fail; + } + + pool->transforms[i] = dce110_transform_create( + ctx, i, &dce110_xfm_offsets[i]); + if (pool->transforms[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create transform!\n"); + goto controller_create_fail; + } + pool->transforms[i]->funcs->transform_set_scaler_filter( + pool->transforms[i], + pool->scaler_filter); + + pool->opps[i] = dce110_opp_create(ctx, i, &dce110_opp_reg_offsets[i]); + if (pool->opps[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error( + "DC: failed to create output pixel processor!\n"); + goto controller_create_fail; + } + } + + audio_init_data.as = adapter_serv; + audio_init_data.ctx = ctx; + pool->audio_count = 0; + for (i = 0; i < pool->controller_count; i++) { + struct graphics_object_id obj_id; + + obj_id = dal_adapter_service_enum_audio_object(adapter_serv, i); + if (false == dal_graphics_object_id_is_valid(obj_id)) { + /* no more valid audio objects */ + break; + } + + audio_init_data.audio_stream_id = obj_id; + pool->audios[i] = dal_audio_create(&audio_init_data); + if (pool->audios[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create DPPs!\n"); + goto audio_create_fail; + } + pool->audio_count++; + } + + for (i = 0; i < pool->stream_enc_count; i++) { + /* TODO: rework fragile code*/ + if (pool->stream_engines.u_all & 1 << i) { + pool->stream_enc[i] = dce110_stream_encoder_create( + i, dc->ctx, + dal_adapter_service_get_bios_parser( + adapter_serv), + &stream_enc_regs[i]); + if (pool->stream_enc[i] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create stream_encoder!\n"); + goto stream_enc_create_fail; + } + } + } + + for (i = 0; i < num_virtual_links; i++) { + pool->stream_enc[pool->stream_enc_count] = + virtual_stream_encoder_create( + dc->ctx, dal_adapter_service_get_bios_parser( + adapter_serv)); + if (pool->stream_enc[pool->stream_enc_count] == NULL) { + BREAK_TO_DEBUGGER(); + dm_error("DC: failed to create stream_encoder!\n"); + goto stream_enc_create_fail; + } + pool->stream_enc_count++; + } + + return true; + +stream_enc_create_fail: + for (i = 0; i < pool->stream_enc_count; i++) { + if (pool->stream_enc[i] != NULL) + dm_free(pool->stream_enc[i]->ctx, + DCE110STRENC_FROM_STRENC(pool->stream_enc[i])); + } + +audio_create_fail: + for (i = 0; i < pool->controller_count; i++) { + if (pool->audios[i] != NULL) + dal_audio_destroy(&pool->audios[i]); + } + +controller_create_fail: + for (i = 0; i < pool->controller_count; i++) { + if (pool->opps[i] != NULL) + dce110_opp_destroy(&pool->opps[i]); + + if (pool->transforms[i] != NULL) + dce110_transform_destroy(&pool->transforms[i]); + + if (pool->ipps[i] != NULL) + dce110_ipp_destroy(&pool->ipps[i]); + + if (pool->mis[i] != NULL) { + dm_free(pool->mis[i]->ctx, + TO_DCE110_MEM_INPUT(pool->mis[i])); + pool->mis[i] = NULL; + } + + if (pool->timing_generators[i] != NULL) { + dm_free(pool->timing_generators[i]->ctx, + DCE110TG_FROM_TG(pool->timing_generators[i])); + pool->timing_generators[i] = NULL; + } + } + +filter_create_fail: + dal_irq_service_destroy(&pool->irqs); + +irqs_create_fail: + dal_display_clock_destroy(&pool->display_clock); + +disp_clk_create_fail: +clk_src_create_fail: + for (i = 0; i < pool->clk_src_count; i++) { + if (pool->clock_sources[i] != NULL) + dce110_clock_source_destroy(&pool->clock_sources[i]); + } + + return false; +} diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h new file mode 100644 index 000000000000..5d60df286835 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_resource.h @@ -0,0 +1,46 @@ +/* +* Copyright 2012-15 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 + * + */ + +#ifndef __DC_RESOURCE_DCE110_H__ +#define __DC_RESOURCE_DCE110_H__ + +#include "core_types.h" + +struct adapter_service; +struct dc; +struct resource_pool; + +bool dce110_construct_resource_pool( + struct adapter_service *adapter_serv, + uint8_t num_virtual_links, + struct dc *dc, + struct resource_pool *pool); + +void dce110_destruct_resource_pool(struct resource_pool *pool); + +void dce110_link_encoder_destroy(struct link_encoder **enc); + +#endif /* __DC_RESOURCE_DCE110_H__ */ +