From patchwork Thu Feb 11 17:19:54 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harry Wentland X-Patchwork-Id: 8283171 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 2C0E2BEEE5 for ; Thu, 11 Feb 2016 17:21:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 896642035D for ; Thu, 11 Feb 2016 17:21:20 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 298F12034C for ; Thu, 11 Feb 2016 17:21:14 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id BD37F7A093; Thu, 11 Feb 2016 09:20:46 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from na01-bn1-obe.outbound.protection.outlook.com (mail-bn1bon0089.outbound.protection.outlook.com [157.56.111.89]) by gabe.freedesktop.org (Postfix) with ESMTPS id 7512F7A096 for ; Thu, 11 Feb 2016 09:20:45 -0800 (PST) Received: from CY1PR1201CA0003.namprd12.prod.outlook.com (10.169.17.141) by SN1PR12MB0718.namprd12.prod.outlook.com (10.163.209.20) with Microsoft SMTP Server (TLS) id 15.1.403.16; Thu, 11 Feb 2016 17:20:43 +0000 Received: from BY2NAM03FT051.eop-NAM03.prod.protection.outlook.com (2a01:111:f400:7e4a::206) by CY1PR1201CA0003.outlook.office365.com (2a01:111:e400:5b9a::13) with Microsoft SMTP Server (TLS) id 15.1.409.15 via Frontend Transport; Thu, 11 Feb 2016 17:20:43 +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 BY2NAM03FT051.mail.protection.outlook.com (10.152.85.169) with Microsoft SMTP Server id 15.1.415.6 via Frontend Transport; Thu, 11 Feb 2016 17:20:42 +0000 X-WSS-ID: 0O2E86H-07-0W2-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 2399812C130F for ; Thu, 11 Feb 2016 12:20:41 -0500 (EST) Received: from SATLEXDAG02.amd.com (10.181.40.5) by SATLVEXEDGE02.amd.com (10.177.96.29) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 11 Feb 2016 11:21:13 -0600 Received: from STOREXDAG03.amd.com (10.1.13.12) by SATLEXDAG02.amd.com (10.181.40.5) with Microsoft SMTP Server (TLS) id 14.3.266.1; Thu, 11 Feb 2016 12:20:41 -0500 Received: from cnhwentlanub.amd.com (172.29.225.36) by storexdag03.amd.com (10.1.13.12) with Microsoft SMTP Server id 14.3.266.1; Thu, 11 Feb 2016 12:20:40 -0500 From: Harry Wentland To: Subject: [PATCH 14/29] drm/amd/dal: Add clock source HW programming Date: Thu, 11 Feb 2016 12:19:54 -0500 Message-ID: <1455211209-26733-15-git-send-email-harry.wentland@amd.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1455211209-26733-1-git-send-email-harry.wentland@amd.com> 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)(6009001)(2980300002)(428002)(199003)(189002)(101416001)(47776003)(76176999)(50986999)(19580405001)(189998001)(2950100001)(48376002)(53416004)(87936001)(229853001)(105586002)(33646002)(77096005)(586003)(36756003)(50466002)(106466001)(19580395003)(2351001)(86362001)(2906002)(4326007)(92566002)(1220700001)(450100001)(11100500001)(5008740100001)(5003940100001)(50226001)(5003600100002)(1096002)(579004); DIR:OUT; SFP:1101; SCL:1; SRVR:SN1PR12MB0718; H:atltwp01.amd.com; FPR:; SPF:None; MLV:sfv; A:1; MX:1; LANG:en; X-MS-Office365-Filtering-Correlation-Id: 2508b0ca-820a-4e08-d6fa-08d33307abd5 X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0718; 2:MZOBGGETFInS6QQhFtmmzi270y5hcDpKMUhZBC5RI2X0RyZnDiZ51FIn0WQVF77EGC8E+ZncGb+m1aaIjMIz/gfax9OXcJdj6jAEIpxqbqXn3PksNCiLX3abIgmpw1bTpTjSeMobB+zgSHqbzEUhDXMQrYbavdpo2B9fC9wq3fT76umBkTOgnwm6h57K8Ljq; 3:eV1R/AbSTucX18Y2jhXN1DoPoo2zRKR3kCRbk+PDZPSUyh844PPFF0JzvtwYzef8N+frSzaykbHOrHYyUCXCvubgkUamQmj0K5y4RBAyEvzXztBi1sGtI+1YwOo3b59Mun9gyp/qFJsmBWIMjD13zap2y1OWEn8fYySnAF7Far7tlqtHCIUXhIsdgk29mp/56cuDOr/H3MC5l/9L17IWHO17R0qoDvjD1jwr50/QFUQ=; 25:JdhssUYrtmSEidlhRk53aIoCCTImdQKfFjRLYEuvPom+n6OpCtqRKQfYgAeHfOZNHwigIPW6M9tTmpf2SigpWVgV3/Ck6hnU5/OIMq41a5TqzsBJJgz8wRYnvFLiQhIRDkmAWpnYKiSwR39hxxtc3nKKy2O8Mp+NqHzQ8Olla+rvXat6UQ7f1EpaAKRJg7Whl0lh54ybQS5Of7XA8ItOlnIp/gw22jCYFLpIx/JduTFll3cE5kdilBEzG41FkkC68EN1OCmy6L/9yKqJx2KFw8W18ms8Jgf1Grdaa47iXYOwFAkzsmv7cxClL5GY368v X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SN1PR12MB0718; X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0718; 20:Z2Y0PNlId7id1+ZIDh7L8bQf4G6i8HrZEiM4oOT6w9pCkYwi3iLyGE2LShV3T9CnPbmRzWOtpEo0cFQcJPpfkO7kGYzXf+94cQWkbyD/LzqmEZ0wYv+msauR+4zBpUKUFq6VNyzfdeCzWzkes9dUJayRYN+13pxIm2T0b0oNk0Tj94ylvtwcJVrYqJJHFPuO7k+RdyCSncQewPoSCswSWQ1fcqsuo4ghWx0I0V6sy7f1i2rKjacwIH5FS8N5/Jxkzxi7CbXh/XvcdizTPCHmD5TeLazPk8MUzTjKNrJeagOQcLqaB9LXPdlJRxNmsXsFKdjYtV+xzVbZxuSXsIL3qKzS+EezS1X7hynHK06Bq5jfrdiSnq52/6NhHjw0IePkpW5CG+HidkkXxjIpvp8c+C5W4SJjPNdtAWcEXu94CYrT71teMejBByTXqOffZ+nwVzFxy5GI+Bex27Xt9bVNKhzmqPerz/WbMH6axZVnOdbsAEzW6u+CaO/KQhJttAuN X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(767451399110); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(13024025)(13018025)(13017025)(13015025)(13023025)(5005006)(8121501046)(10201501046)(3002001); SRVR:SN1PR12MB0718; BCL:0; PCL:0; RULEID:; SRVR:SN1PR12MB0718; X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0718; 4:NSlYC1mQt6B+Kp8KfmrenB1lDrPmj+RqHfxW8i7iW82Yfy5kGq/AI48zQJSxZHTSWwRDaNW3RQTFf7BzNsUg4U4cmItIhQerO9pX22HP64ggyTUBvtZYl+lGkOc29sbUjkdyu2P40kehyQD8It4GNclkvo3/ECC78bIZsRbln0r7HRv//Opvcvx81K27YVjHAHTz1hyCKSb+hQdktBgvC1TMYfHQcwUrZn19vPgQ+EmB+KS4TfDxVJnlNX+UYFIfuYB9tL6ch10s/NCELWoBbY9Skdx4slpP+O8vwd6ws/W2rbV6D7EJV807pJrz6lHrqBQo31AkLVvZYk8acKF3AaoyC82Z1Qw6Qh5KbrsktqtscHn8dj6NL09OAs0yjhZlYNGrWppJQ9QFtyW0o0y8OfPBLxs605bOjyL4KUXVKERstsqHiJlC+2haCF3Tj10+m8hyIdDMQsqXJOvea9qB/wBebbqicS8g2VUfBBao6FArru/59YnfOiS66dbGFtKO X-Forefront-PRVS: 08497C3D99 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; SN1PR12MB0718; 23:UL2+Scjhxjx/L4ZmOMeWcYJbb03lK3SE/UUnv1g3k?= =?us-ascii?Q?Eb36kRX9QqoEZVu6z1TIgiiqnvQrL0knSYIrJOjrbEOiD4OaCyIPdBEdtTLM?= =?us-ascii?Q?lN28sZX8jXu3Xti7FzhyZhwllYY7xvBmPCMQScyJel8GxZBUk98L92iL3bGJ?= =?us-ascii?Q?fcLvV3DcXQ5OvdHF9NSmWQSnSvFjhqD2A0Wj39Kkb8S80VgdI9k3UHw7TSos?= =?us-ascii?Q?l+Okbvers+GYHWh9AUXUwg9ClXVfkee7lcxLhrP1+exyj7raoMfbiBElyObg?= =?us-ascii?Q?QoZZIp2Dldk6MuLGudMMfoI+157JsktAN2rIF1cJmEWA6g78FjIbwTzU6g70?= =?us-ascii?Q?892K/CGH/pE8uiuHYekpm2bU2mdX7n+wHGCdV5rLIj4m1kI/h0UuK/rw1W9W?= =?us-ascii?Q?wJTEycjYy0047uynvLUUHCW7doj35tYljL4UAX4FxJUv7OkVfVfvT+o52VYu?= =?us-ascii?Q?sieX2aE5xZdUbfIBI5zcABPvNtkZQcm+hsqkzcCf0HSs5Ins3vLbqm1UMVir?= =?us-ascii?Q?ngjtXO81A6S0fSXsuMCn1udzo7jl53zxzjdctSxtqZvbKaC9M6xyTh2DjoAK?= =?us-ascii?Q?wAO01rQn9vKHkjjcEMJJa0kzOhXDRT11wmmFl7AdhsdWih/3EOlc6OVF5NzH?= =?us-ascii?Q?dcQfPjkhyQBV0rLqckkFu4gbyr43vBm6aQUcqbVIbDxjUCQOtSGqYs7Rwn71?= =?us-ascii?Q?rm1/d3IA4BGGMhdxMUYKU8uJMqXApPrlZ3nkwR6ndnTfDR+Am9010L+ijJV8?= =?us-ascii?Q?lSRWwMReqRMznXhwIIYJAFhpB7TVGblx5WJkJ5XyMtHs6BOidQsXtxXD872d?= =?us-ascii?Q?LS66TIKjD8py/ZVoYJ8NurCwcxHAmDvCAX4l4vw81Tx7thXVzsk6mvC+8g91?= =?us-ascii?Q?oP+ut7ifeeDSEi1jUP5PUOHFWAHMgsANNy7fguZ0R3pswvYrqPwpY6L1P02s?= =?us-ascii?Q?0b89K4J9qusM5k2CUvdOXOaGeRfJMBy96RA3/KPQrfj9lpAS/11fOx8oQCA0?= =?us-ascii?Q?4nJ3EbKX+5TdmZucZTxDYG3?= X-Microsoft-Exchange-Diagnostics: 1; SN1PR12MB0718; 5:HXee6SVrjjwBeDVS99JDlmNw1kwQY/nCYthBC5hynWusry3yxVdJ+gJTxBG293AHYxMHnqtY9U8DXNEs5OTETg7aIgL117F6X85+IogZq/geVeegF5k1y1AELlksfNuvDVriYWlHhI7NhXchmhJ6Hg==; 24:Bw2VmMO0CFEsKZoJDGQ6Hl3JFbQUrxez8/ighf5XUlShNuYsSE4KdL1UqE6u5zj78Fyw65vsiQELsQScD58t3yhSqPhoKzfXwksrvrhD0iM=; 20:2u4CYwiFZ9q3SXMfhTGdqXxfbGpJBP09WAD1r0/P17az+Jq3h5Tmn7kbCINV40nftKx3cqi2Q98IW5iKSDruRJDdkiVO2kxA4SLoAf02ds9JVKfrHyK/kZPvjZiLLKesn3AT1Nc/Lb1Ob2V8eOSykuemq+wZLd2LV1THeZ1Vv3O7hJdhS0KV3DBVWQRq7ZJOid//JHyjlPjfPpoDdbXfaYNjZVryj8F926faTLOgPoKcGxoIVrH9OAk8NeqkJQCm SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Feb 2016 17:20:42.8358 (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: SN1PR12MB0718 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.4 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 pixel clock programming and functionality to power down clock sources. Signed-off-by: Harry Wentland Reviewed-by: Alex Deucher --- .../drm/amd/dal/dc/dce110/dce110_clock_source.c | 1162 ++++++++++++++++++++ .../drm/amd/dal/dc/dce110/dce110_clock_source.h | 64 ++ 2 files changed, 1226 insertions(+) create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c create mode 100644 drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c new file mode 100644 index 000000000000..e1bac1f77b79 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.c @@ -0,0 +1,1162 @@ +/* + * 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 DCE11 register header files */ +#include "dce/dce_11_0_d.h" +#include "dce/dce_11_0_sh_mask.h" + +#include "dc_types.h" +#include "core_types.h" + +#include "include/grph_object_id.h" +#include "include/logger_interface.h" + +#include "dce110_clock_source.h" + +#define FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM 6 +#define CALC_PLL_CLK_SRC_ERR_TOLERANCE 1 +#define MAX_PLL_CALC_ERROR 0xFFFFFFFF + +static const struct spread_spectrum_data *get_ss_data_entry( + struct dce110_clk_src *clk_src, + enum signal_type signal, + uint32_t pix_clk_khz) +{ + + uint32_t entrys_num; + uint32_t i; + struct spread_spectrum_data *ss_parm = NULL; + struct spread_spectrum_data *ret = NULL; + + switch (signal) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + ss_parm = clk_src->dvi_ss_params; + entrys_num = clk_src->dvi_ss_params_cnt; + break; + + case SIGNAL_TYPE_HDMI_TYPE_A: + ss_parm = clk_src->hdmi_ss_params; + entrys_num = clk_src->hdmi_ss_params_cnt; + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + ss_parm = clk_src->dp_ss_params; + entrys_num = clk_src->dp_ss_params_cnt; + break; + + default: + ss_parm = NULL; + entrys_num = 0; + break; + } + + if (ss_parm == NULL) + return ret; + + for (i = 0; i < entrys_num; ++i, ++ss_parm) { + if (ss_parm->freq_range_khz >= pix_clk_khz) { + ret = ss_parm; + break; + } + } + + return ret; +} + +/** +* Function: calculate_fb_and_fractional_fb_divider +* +* * DESCRIPTION: Calculates feedback and fractional feedback dividers values +* +*PARAMETERS: +* targetPixelClock Desired frequency in 10 KHz +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* feedback_divider_param Pointer where to store +* calculated feedback divider value +* fract_feedback_divider_param Pointer where to store +* calculated fract feedback divider value +* +*RETURNS: +* It fills the locations pointed by feedback_divider_param +* and fract_feedback_divider_param +* It returns - true if feedback divider not 0 +* - false should never happen) +*/ +static bool calculate_fb_and_fractional_fb_divider( + struct calc_pll_clock_source *calc_pll_cs, + uint32_t target_pix_clk_khz, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t *feedback_divider_param, + uint32_t *fract_feedback_divider_param) +{ + uint64_t feedback_divider; + + feedback_divider = + (uint64_t)(target_pix_clk_khz * ref_divider * post_divider); + feedback_divider *= 10; + /* additional factor, since we divide by 10 afterwards */ + feedback_divider *= (uint64_t)(calc_pll_cs->fract_fb_divider_factor); + feedback_divider = div_u64(feedback_divider, calc_pll_cs->ref_freq_khz); + +/*Round to the number of precision + * The following code replace the old code (ullfeedbackDivider + 5)/10 + * for example if the difference between the number + * of fractional feedback decimal point and the fractional FB Divider precision + * is 2 then the equation becomes (ullfeedbackDivider + 5*100) / (10*100))*/ + + feedback_divider += (uint64_t) + (5 * calc_pll_cs->fract_fb_divider_precision_factor); + feedback_divider = + div_u64(feedback_divider, + calc_pll_cs->fract_fb_divider_precision_factor * 10); + feedback_divider *= (uint64_t) + (calc_pll_cs->fract_fb_divider_precision_factor); + + *feedback_divider_param = + div_u64_rem( + feedback_divider, + calc_pll_cs->fract_fb_divider_factor, + fract_feedback_divider_param); + + if (*feedback_divider_param != 0) + return true; + return false; +} + +/** +*calc_fb_divider_checking_tolerance +* +*DESCRIPTION: Calculates Feedback and Fractional Feedback divider values +* for passed Reference and Post divider, checking for tolerance. +*PARAMETERS: +* pll_settings Pointer to structure +* ref_divider Reference divider (already known) +* postDivider Post Divider (already known) +* tolerance Tolerance for Calculated Pixel Clock to be within +* +*RETURNS: +* It fills the PLLSettings structure with PLL Dividers values +* if calculated values are within required tolerance +* It returns - true if eror is within tolerance +* - false if eror is not within tolerance +*/ +static bool calc_fb_divider_checking_tolerance( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t ref_divider, + uint32_t post_divider, + uint32_t tolerance) +{ + uint32_t feedback_divider; + uint32_t fract_feedback_divider; + uint32_t actual_calculated_clock_khz; + uint32_t abs_err; + uint64_t actual_calc_clk_khz; + + calculate_fb_and_fractional_fb_divider( + calc_pll_cs, + pll_settings->adjusted_pix_clk, + ref_divider, + post_divider, + &feedback_divider, + &fract_feedback_divider); + + /*Actual calculated value*/ + actual_calc_clk_khz = (uint64_t)(feedback_divider * + calc_pll_cs->fract_fb_divider_factor) + + fract_feedback_divider; + actual_calc_clk_khz *= calc_pll_cs->ref_freq_khz; + actual_calc_clk_khz = + div_u64(actual_calc_clk_khz, + ref_divider * post_divider * + calc_pll_cs->fract_fb_divider_factor); + + actual_calculated_clock_khz = (uint32_t)(actual_calc_clk_khz); + + abs_err = (actual_calculated_clock_khz > + pll_settings->adjusted_pix_clk) + ? actual_calculated_clock_khz - + pll_settings->adjusted_pix_clk + : pll_settings->adjusted_pix_clk - + actual_calculated_clock_khz; + + if (abs_err <= tolerance) { + /*found good values*/ + pll_settings->reference_freq = calc_pll_cs->ref_freq_khz; + pll_settings->reference_divider = ref_divider; + pll_settings->feedback_divider = feedback_divider; + pll_settings->fract_feedback_divider = fract_feedback_divider; + pll_settings->pix_clk_post_divider = post_divider; + pll_settings->calculated_pix_clk = + actual_calculated_clock_khz; + pll_settings->vco_freq = + actual_calculated_clock_khz * post_divider; + return true; + } + return false; +} + + +static bool calc_pll_dividers_in_range( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings, + uint32_t min_ref_divider, + uint32_t max_ref_divider, + uint32_t min_post_divider, + uint32_t max_post_divider, + uint32_t err_tolerance) +{ + uint32_t ref_divider; + uint32_t post_divider; + uint32_t tolerance; + +/* This is err_tolerance / 10000 = 0.0025 - acceptable error of 0.25% + * This is errorTolerance / 10000 = 0.0001 - acceptable error of 0.01%*/ + tolerance = (pll_settings->adjusted_pix_clk * err_tolerance) / + 10000; + if (tolerance < CALC_PLL_CLK_SRC_ERR_TOLERANCE) + tolerance = CALC_PLL_CLK_SRC_ERR_TOLERANCE; + + for ( + post_divider = max_post_divider; + post_divider >= min_post_divider; + --post_divider) { + for ( + ref_divider = min_ref_divider; + ref_divider <= max_ref_divider; + ++ref_divider) { + if (calc_fb_divider_checking_tolerance( + calc_pll_cs, + pll_settings, + ref_divider, + post_divider, + tolerance)) { + return true; + } + } + } + + return false; +} + +static uint32_t calculate_pixel_clock_pll_dividers( + struct calc_pll_clock_source *calc_pll_cs, + struct pll_settings *pll_settings) +{ + uint32_t err_tolerance; + uint32_t min_post_divider; + uint32_t max_post_divider; + uint32_t min_ref_divider; + uint32_t max_ref_divider; + + if (pll_settings->adjusted_pix_clk == 0) { + dal_logger_write(calc_pll_cs->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s Bad requested pixel clock", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 1) Find Post divider ranges */ + if (pll_settings->pix_clk_post_divider) { + min_post_divider = pll_settings->pix_clk_post_divider; + max_post_divider = pll_settings->pix_clk_post_divider; + } else { + min_post_divider = calc_pll_cs->min_pix_clock_pll_post_divider; + if (min_post_divider * pll_settings->adjusted_pix_clk < + calc_pll_cs->min_vco_khz) { + min_post_divider = calc_pll_cs->min_vco_khz / + pll_settings->adjusted_pix_clk; + if ((min_post_divider * + pll_settings->adjusted_pix_clk) < + calc_pll_cs->min_vco_khz) + min_post_divider++; + } + + max_post_divider = calc_pll_cs->max_pix_clock_pll_post_divider; + if (max_post_divider * pll_settings->adjusted_pix_clk + > calc_pll_cs->max_vco_khz) + max_post_divider = calc_pll_cs->max_vco_khz / + pll_settings->adjusted_pix_clk; + } + +/* 2) Find Reference divider ranges + * When SS is enabled, or for Display Port even without SS, + * pll_settings->referenceDivider is not zero. + * So calculate PPLL FB and fractional FB divider + * using the passed reference divider*/ + + if (pll_settings->reference_divider) { + min_ref_divider = pll_settings->reference_divider; + max_ref_divider = pll_settings->reference_divider; + } else { + min_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz) + > calc_pll_cs->min_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz + / calc_pll_cs->max_pll_input_freq_khz + : calc_pll_cs->min_pll_ref_divider; + + max_ref_divider = ((calc_pll_cs->ref_freq_khz + / calc_pll_cs->min_pll_input_freq_khz) + < calc_pll_cs->max_pll_ref_divider) + ? calc_pll_cs->ref_freq_khz / + calc_pll_cs->min_pll_input_freq_khz + : calc_pll_cs->max_pll_ref_divider; + } + +/* If some parameters are invalid we could have scenario when "min">"max" + * which produced endless loop later. + * We should investigate why we get the wrong parameters. + * But to follow the similar logic when "adjustedPixelClock" is set to be 0 + * it is better to return here than cause system hang/watchdog timeout later. + * ## SVS Wed 15 Jul 2009 */ + + if (min_post_divider > max_post_divider) { + dal_logger_write(calc_pll_cs->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s Post divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + + if (min_ref_divider > max_ref_divider) { + dal_logger_write(calc_pll_cs->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s Reference divider range is invalid", __func__); + return MAX_PLL_CALC_ERROR; + } + +/* 3) Try to find PLL dividers given ranges + * starting with minimal error tolerance. + * Increase error tolerance until PLL dividers found*/ + err_tolerance = MAX_PLL_CALC_ERROR; + + while (!calc_pll_dividers_in_range( + calc_pll_cs, + pll_settings, + min_ref_divider, + max_ref_divider, + min_post_divider, + max_post_divider, + err_tolerance)) + err_tolerance += (err_tolerance > 10) + ? (err_tolerance / 10) + : 1; + + return err_tolerance; +} + +static bool pll_adjust_pix_clk( + struct dce110_clk_src *clk_src, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + uint32_t actual_pix_clk_khz = 0; + uint32_t requested_clk_khz = 0; + struct bp_adjust_pixel_clock_parameters bp_adjust_pixel_clock_params = { + 0 }; + enum bp_result bp_result; + + switch (pix_clk_params->signal_type) { + case SIGNAL_TYPE_HDMI_TYPE_A: { + requested_clk_khz = pix_clk_params->requested_pix_clk; + + switch (pix_clk_params->color_depth) { + case COLOR_DEPTH_101010: + requested_clk_khz = (requested_clk_khz * 5) >> 2; + break; /* x1.25*/ + case COLOR_DEPTH_121212: + requested_clk_khz = (requested_clk_khz * 6) >> 2; + break; /* x1.5*/ + case COLOR_DEPTH_161616: + requested_clk_khz = requested_clk_khz * 2; + break; /* x2.0*/ + default: + break; + } + + actual_pix_clk_khz = requested_clk_khz; + } + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_EDP: + requested_clk_khz = pix_clk_params->requested_sym_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + + default: + requested_clk_khz = pix_clk_params->requested_pix_clk; + actual_pix_clk_khz = pix_clk_params->requested_pix_clk; + break; + } + + bp_adjust_pixel_clock_params.pixel_clock = requested_clk_khz; + bp_adjust_pixel_clock_params. + encoder_object_id = pix_clk_params->encoder_object_id; + bp_adjust_pixel_clock_params.signal_type = pix_clk_params->signal_type; + bp_adjust_pixel_clock_params. + ss_enable = pix_clk_params->flags.ENABLE_SS; + bp_result = clk_src->bios->funcs->adjust_pixel_clock( + clk_src->bios, &bp_adjust_pixel_clock_params); + if (bp_result == BP_RESULT_OK) { + pll_settings->actual_pix_clk = actual_pix_clk_khz; + pll_settings->adjusted_pix_clk = + bp_adjust_pixel_clock_params.adjusted_pixel_clock; + pll_settings->reference_divider = + bp_adjust_pixel_clock_params.reference_divider; + pll_settings->pix_clk_post_divider = + bp_adjust_pixel_clock_params.pixel_clock_post_divider; + + return true; + } + + return false; +} + +/** + * Calculate PLL Dividers for given Clock Value. + * First will call VBIOS Adjust Exec table to check if requested Pixel clock + * will be Adjusted based on usage. + * Then it will calculate PLL Dividers for this Adjusted clock using preferred + * method (Maximum VCO frequency). + * + * \return + * Calculation error in units of 0.01% + */ +static uint32_t dce110_get_pix_clk_dividers( + struct clock_source *cs, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); + uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; + uint32_t addr = 0; + uint32_t value = 0; + uint32_t field = 0; + + if (pix_clk_params == NULL || pll_settings == NULL + || pix_clk_params->requested_pix_clk == 0) { + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s: Invalid parameters!!\n", __func__); + return pll_calc_error; + } + + dm_memset(pll_settings, 0, sizeof(*pll_settings)); + + if (cs->id == CLOCK_SOURCE_ID_EXTERNAL) { + pll_settings->adjusted_pix_clk = clk_src->ext_clk_khz; + pll_settings->calculated_pix_clk = clk_src->ext_clk_khz; + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + return 0; + } + /* PLL only after this point */ + + /* Check if reference clock is external (not pcie/xtalin) + * HW Dce80 spec: + * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB + * 04 - HSYNCA, 05 - GENLK_CLK, 06 - PCIE_REFCLK, 07 - DVOCLK0 */ + addr = clk_src->offsets.pll_cntl; + value = dm_read_reg(clk_src->base.ctx, addr); + field = get_reg_field_value(value, PLL_CNTL, PLL_REF_DIV_SRC); + pll_settings->use_external_clk = (field > 1); + + /* VBIOS by default enables DP SS (spread on IDCLK) for DCE 8.0 always + * (we do not care any more from SI for some older DP Sink which + * does not report SS support, no known issues) */ + if ((pix_clk_params->flags.ENABLE_SS) || + (dc_is_dp_signal(pix_clk_params->signal_type))) { + + const struct spread_spectrum_data *ss_data = get_ss_data_entry( + clk_src, + pix_clk_params->signal_type, + pll_settings->adjusted_pix_clk); + + if (NULL != ss_data) + pll_settings->ss_percentage = ss_data->percentage; + } + + /* Check VBIOS AdjustPixelClock Exec table */ + if (!pll_adjust_pix_clk(clk_src, pix_clk_params, pll_settings)) { + /* Should never happen, ASSERT and fill up values to be able + * to continue. */ + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "%s: Failed to adjust pixel clock!!", __func__); + pll_settings->actual_pix_clk = + pix_clk_params->requested_pix_clk; + pll_settings->adjusted_pix_clk = + pix_clk_params->requested_pix_clk; + + if (dc_is_dp_signal(pix_clk_params->signal_type)) + pll_settings->adjusted_pix_clk = 100000; + } + + /* Calculate Dividers */ + if (pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) + /*Calculate Dividers by HDMI object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll_hdmi, + pll_settings); + else + /*Calculate Dividers by default object, no SS case or SS case */ + pll_calc_error = + calculate_pixel_clock_pll_dividers( + &clk_src->calc_pll, + pll_settings); + + return pll_calc_error; +} + +static bool disable_spread_spectrum(struct dce110_clk_src *clk_src) +{ + enum bp_result result; + struct bp_spread_spectrum_parameters bp_ss_params = {0}; + + bp_ss_params.pll_id = clk_src->base.id; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + result = clk_src->bios->funcs->enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_ss_params, + false); + + return result == BP_RESULT_OK; +} + +static bool calculate_ss( + const struct pll_settings *pll_settings, + const struct spread_spectrum_data *ss_data, + struct delta_sigma_data *ds_data) +{ + struct fixed32_32 fb_div; + struct fixed32_32 ss_amount; + struct fixed32_32 ss_nslip_amount; + struct fixed32_32 ss_ds_frac_amount; + struct fixed32_32 ss_step_size; + struct fixed32_32 modulation_time; + + if (ds_data == NULL) + return false; + if (ss_data == NULL) + return false; + if (ss_data->percentage == 0) + return false; + if (pll_settings == NULL) + return false; + + + dm_memset(ds_data, 0, sizeof(struct delta_sigma_data)); + + + + /* compute SS_AMOUNT_FBDIV & SS_AMOUNT_NFRAC_SLIP & SS_AMOUNT_DSFRAC*/ + /* 6 decimal point support in fractional feedback divider */ + fb_div = dal_fixed32_32_from_fraction( + pll_settings->fract_feedback_divider, 1000000); + fb_div = dal_fixed32_32_add_int(fb_div, pll_settings->feedback_divider); + + ds_data->ds_frac_amount = 0; + /*spreadSpectrumPercentage is in the unit of .01%, + * so have to divided by 100 * 100*/ + ss_amount = dal_fixed32_32_mul( + fb_div, dal_fixed32_32_from_fraction(ss_data->percentage, + 100 * ss_data->percentage_divider)); + ds_data->feedback_amount = dal_fixed32_32_floor(ss_amount); + + ss_nslip_amount = dal_fixed32_32_sub(ss_amount, + dal_fixed32_32_from_int(ds_data->feedback_amount)); + ss_nslip_amount = dal_fixed32_32_mul_int(ss_nslip_amount, 10); + ds_data->nfrac_amount = dal_fixed32_32_floor(ss_nslip_amount); + + ss_ds_frac_amount = dal_fixed32_32_sub(ss_nslip_amount, + dal_fixed32_32_from_int(ds_data->nfrac_amount)); + ss_ds_frac_amount = dal_fixed32_32_mul_int(ss_ds_frac_amount, 65536); + ds_data->ds_frac_amount = dal_fixed32_32_floor(ss_ds_frac_amount); + + /* compute SS_STEP_SIZE_DSFRAC */ + modulation_time = dal_fixed32_32_from_fraction( + pll_settings->reference_freq * 1000, + pll_settings->reference_divider * ss_data->modulation_freq_hz); + + + if (ss_data->flags.CENTER_SPREAD) + modulation_time = dal_fixed32_32_div_int(modulation_time, 4); + else + modulation_time = dal_fixed32_32_div_int(modulation_time, 2); + + ss_step_size = dal_fixed32_32_div(ss_amount, modulation_time); + /* SS_STEP_SIZE_DSFRAC_DEC = Int(SS_STEP_SIZE * 2 ^ 16 * 10)*/ + ss_step_size = dal_fixed32_32_mul_int(ss_step_size, 65536 * 10); + ds_data->ds_frac_size = dal_fixed32_32_floor(ss_step_size); + + return true; +} + +static bool enable_spread_spectrum( + struct dce110_clk_src *clk_src, + enum signal_type signal, struct pll_settings *pll_settings) +{ + struct bp_spread_spectrum_parameters bp_params = {0}; + struct delta_sigma_data d_s_data; + const struct spread_spectrum_data *ss_data = NULL; + + ss_data = get_ss_data_entry( + clk_src, + signal, + pll_settings->calculated_pix_clk); + +/* Pixel clock PLL has been programmed to generate desired pixel clock, + * now enable SS on pixel clock */ +/* TODO is it OK to return true not doing anything ??*/ + if (ss_data != NULL && pll_settings->ss_percentage != 0) { + if (calculate_ss(pll_settings, ss_data, &d_s_data)) { + bp_params.ds.feedback_amount = + d_s_data.feedback_amount; + bp_params.ds.nfrac_amount = + d_s_data.nfrac_amount; + bp_params.ds.ds_frac_size = d_s_data.ds_frac_size; + bp_params.ds_frac_amount = + d_s_data.ds_frac_amount; + bp_params.flags.DS_TYPE = 1; + bp_params.pll_id = clk_src->base.id; + bp_params.percentage = ss_data->percentage; + if (ss_data->flags.CENTER_SPREAD) + bp_params.flags.CENTER_SPREAD = 1; + if (ss_data->flags.EXTERNAL_SS) + bp_params.flags.EXTERNAL_SS = 1; + + if (BP_RESULT_OK != + clk_src->bios->funcs-> + enable_spread_spectrum_on_ppll( + clk_src->bios, + &bp_params, + true)) + return false; + } else + return false; + } + return true; +} + +static void program_pixel_clk_resync( + struct dce110_clk_src *clk_src, + enum signal_type signal_type, + enum dc_color_depth colordepth) +{ + uint32_t value = 0; + + value = dm_read_reg(clk_src->base.ctx, clk_src->offsets.pixclk_resync_cntl); + + set_reg_field_value( + value, + 0, + PIXCLK1_RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1); + + /* + 24 bit mode: TMDS clock = 1.0 x pixel clock (1:1) + 30 bit mode: TMDS clock = 1.25 x pixel clock (5:4) + 36 bit mode: TMDS clock = 1.5 x pixel clock (3:2) + 48 bit mode: TMDS clock = 2 x pixel clock (2:1) + */ + if (signal_type != SIGNAL_TYPE_HDMI_TYPE_A) + return; + + switch (colordepth) { + case COLOR_DEPTH_888: + set_reg_field_value( + value, + 0, + PIXCLK1_RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1); + break; + case COLOR_DEPTH_101010: + set_reg_field_value( + value, + 1, + PIXCLK1_RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1); + break; + case COLOR_DEPTH_121212: + set_reg_field_value( + value, + 2, + PIXCLK1_RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1); + break; + case COLOR_DEPTH_161616: + set_reg_field_value( + value, + 3, + PIXCLK1_RESYNC_CNTL, + DCCG_DEEP_COLOR_CNTL1); + break; + default: + break; + } + + dm_write_reg( + clk_src->base.ctx, + clk_src->offsets.pixclk_resync_cntl, + value); +} + +static bool dce110_program_pix_clk( + struct clock_source *clk_src, + struct pixel_clk_params *pix_clk_params, + struct pll_settings *pll_settings) +{ + struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); + struct bp_pixel_clock_parameters bp_pc_params = {0}; + + /* First disable SS + * ATOMBIOS will enable by default SS on PLL for DP, + * do not disable it here + */ + if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL && + !dc_is_dp_signal(pix_clk_params->signal_type)) + disable_spread_spectrum(dce110_clk_src); + + /*ATOMBIOS expects pixel rate adjusted by deep color ratio)*/ + bp_pc_params.controller_id = pix_clk_params->controller_id; + bp_pc_params.pll_id = clk_src->id; + bp_pc_params.target_pixel_clock = + pll_settings->actual_pix_clk; + bp_pc_params.reference_divider = pll_settings->reference_divider; + bp_pc_params.feedback_divider = pll_settings->feedback_divider; + bp_pc_params.fractional_feedback_divider = + pll_settings->fract_feedback_divider; + bp_pc_params.pixel_clock_post_divider = + pll_settings->pix_clk_post_divider; + bp_pc_params.encoder_object_id = pix_clk_params->encoder_object_id; + bp_pc_params.signal_type = pix_clk_params->signal_type; + bp_pc_params.flags.SET_EXTERNAL_REF_DIV_SRC = + pll_settings->use_external_clk; + + if (dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, &bp_pc_params) != BP_RESULT_OK) + return false; + +/* Enable SS + * ATOMBIOS will enable by default SS for DP on PLL ( DP ID clock), + * based on HW display PLL team, SS control settings should be programmed + * during PLL Reset, but they do not have effect + * until SS_EN is asserted.*/ + if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL + && pix_clk_params->flags.ENABLE_SS && !dc_is_dp_signal( + pix_clk_params->signal_type)) + if (!enable_spread_spectrum(dce110_clk_src, + pix_clk_params->signal_type, + pll_settings)) + return false; + +/* Resync deep color DTO */ + if (clk_src->id != CLOCK_SOURCE_ID_EXTERNAL) + program_pixel_clk_resync(dce110_clk_src, + pix_clk_params->signal_type, + pix_clk_params->color_depth); + + return true; +} + +static bool dce110_clock_source_power_down( + struct clock_source *clk_src) +{ + struct dce110_clk_src *dce110_clk_src = TO_DCE110_CLK_SRC(clk_src); + enum bp_result bp_result; + struct bp_pixel_clock_parameters bp_pixel_clock_params = {0}; + + if (clk_src->id == CLOCK_SOURCE_ID_EXTERNAL) + return true; + + /* If Pixel Clock is 0 it means Power Down Pll*/ + bp_pixel_clock_params.controller_id = CONTROLLER_ID_UNDEFINED; + bp_pixel_clock_params.pll_id = clk_src->id; + bp_pixel_clock_params.flags.FORCE_PROGRAMMING_OF_PLL = 1; + + /*Call ASICControl to process ATOMBIOS Exec table*/ + bp_result = dce110_clk_src->bios->funcs->set_pixel_clock( + dce110_clk_src->bios, + &bp_pixel_clock_params); + + return bp_result == BP_RESULT_OK; +} + +/*****************************************/ +/* Constructor */ +/*****************************************/ +static struct clock_source_funcs dce110_clk_src_funcs = { + .cs_power_down = dce110_clock_source_power_down, + .program_pix_clk = dce110_program_pix_clk, + .get_pix_clk_dividers = dce110_get_pix_clk_dividers +}; + + +static void get_ss_info_from_atombios( + struct dce110_clk_src *clk_src, + enum as_signal_type as_signal, + struct spread_spectrum_data *spread_spectrum_data[], + uint32_t *ss_entries_num) +{ + enum bp_result bp_result = BP_RESULT_FAILURE; + struct spread_spectrum_info *ss_info; + struct spread_spectrum_data *ss_data; + struct spread_spectrum_info *ss_info_cur; + struct spread_spectrum_data *ss_data_cur; + uint32_t i; + + if (ss_entries_num == NULL) { + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_SYNC, + LOG_MINOR_SYNC_HW_CLOCK_ADJUST, + "Invalid entry !!!\n"); + return; + } + if (spread_spectrum_data == NULL) { + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_SYNC, + LOG_MINOR_SYNC_HW_CLOCK_ADJUST, + "Invalid array pointer!!!\n"); + return; + } + + spread_spectrum_data[0] = NULL; + *ss_entries_num = 0; + + *ss_entries_num = clk_src->bios->funcs->get_ss_entry_number( + clk_src->bios, + as_signal); + + if (*ss_entries_num == 0) + return; + + ss_info = dm_alloc(clk_src->base.ctx, sizeof(struct spread_spectrum_info) + * (*ss_entries_num)); + ss_info_cur = ss_info; + if (ss_info == NULL) + return; + + ss_data = dm_alloc(clk_src->base.ctx, sizeof(struct spread_spectrum_data) * + (*ss_entries_num)); + if (ss_data == NULL) + goto out_free_info; + + for (i = 0, ss_info_cur = ss_info; + i < (*ss_entries_num); + ++i, ++ss_info_cur) { + + bp_result = clk_src->bios->funcs->get_spread_spectrum_info( + clk_src->bios, + as_signal, + i, + ss_info_cur); + + if (bp_result != BP_RESULT_OK) + goto out_free_data; + } + + for (i = 0, ss_info_cur = ss_info, ss_data_cur = ss_data; + i < (*ss_entries_num); + ++i, ++ss_info_cur, ++ss_data_cur) { + + if (ss_info_cur->type.STEP_AND_DELAY_INFO != false) { + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_SYNC, + LOG_MINOR_SYNC_HW_CLOCK_ADJUST, + "Invalid ATOMBIOS SS Table!!!\n"); + goto out_free_data; + } + + /* for HDMI check SS percentage, + * if it is > 6 (0.06%), the ATOMBIOS table info is invalid*/ + if (as_signal == AS_SIGNAL_TYPE_HDMI + && ss_info_cur->spread_spectrum_percentage > 6){ + /* invalid input, do nothing */ + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_SYNC, + LOG_MINOR_SYNC_HW_CLOCK_ADJUST, + "Invalid SS percentage "); + dal_logger_write(clk_src->base.ctx->logger, + LOG_MAJOR_SYNC, + LOG_MINOR_SYNC_HW_CLOCK_ADJUST, + "for HDMI in ATOMBIOS info Table!!!\n"); + continue; + } + if (ss_info_cur->spread_percentage_divider == 1000) { + /* Keep previous precision from ATOMBIOS for these + * in case new precision set by ATOMBIOS for these + * (otherwise all code in DCE specific classes + * for all previous ASICs would need + * to be updated for SS calculations, + * Audio SS compensation and DP DTO SS compensation + * which assumes fixed SS percentage Divider = 100)*/ + ss_info_cur->spread_spectrum_percentage /= 10; + ss_info_cur->spread_percentage_divider = 100; + } + + ss_data_cur->freq_range_khz = ss_info_cur->target_clock_range; + ss_data_cur->percentage = + ss_info_cur->spread_spectrum_percentage; + ss_data_cur->percentage_divider = + ss_info_cur->spread_percentage_divider; + ss_data_cur->modulation_freq_hz = + ss_info_cur->spread_spectrum_range; + + if (ss_info_cur->type.CENTER_MODE) + ss_data_cur->flags.CENTER_SPREAD = 1; + + if (ss_info_cur->type.EXTERNAL) + ss_data_cur->flags.EXTERNAL_SS = 1; + + } + + *spread_spectrum_data = ss_data; + dm_free(clk_src->base.ctx, ss_info); + return; + +out_free_data: + dm_free(clk_src->base.ctx, ss_data); + *ss_entries_num = 0; +out_free_info: + dm_free(clk_src->base.ctx, ss_info); +} + +static void ss_info_from_atombios_create( + struct dce110_clk_src *clk_src) +{ + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DISPLAY_PORT, + &clk_src->dp_ss_params, + &clk_src->dp_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_HDMI, + &clk_src->hdmi_ss_params, + &clk_src->hdmi_ss_params_cnt); + get_ss_info_from_atombios( + clk_src, + AS_SIGNAL_TYPE_DVI, + &clk_src->dvi_ss_params, + &clk_src->dvi_ss_params_cnt); +} + +static bool calc_pll_max_vco_construct( + struct calc_pll_clock_source *calc_pll_cs, + struct calc_pll_clock_source_init_data *init_data) +{ + uint32_t i; + struct firmware_info fw_info = { { 0 } }; + if (calc_pll_cs == NULL || + init_data == NULL || + init_data->bp == NULL) + return false; + + if (init_data->bp->funcs->get_firmware_info( + init_data->bp, + &fw_info) != BP_RESULT_OK) + return false; + + calc_pll_cs->ctx = init_data->ctx; + calc_pll_cs->ref_freq_khz = fw_info.pll_info.crystal_frequency; + calc_pll_cs->min_vco_khz = + fw_info.pll_info.min_output_pxl_clk_pll_frequency; + calc_pll_cs->max_vco_khz = + fw_info.pll_info.max_output_pxl_clk_pll_frequency; + + if (init_data->max_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->max_pll_input_freq_khz = + init_data->max_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->max_pll_input_freq_khz = + fw_info.pll_info.max_input_pxl_clk_pll_frequency; + + if (init_data->min_override_input_pxl_clk_pll_freq_khz != 0) + calc_pll_cs->min_pll_input_freq_khz = + init_data->min_override_input_pxl_clk_pll_freq_khz; + else + calc_pll_cs->min_pll_input_freq_khz = + fw_info.pll_info.min_input_pxl_clk_pll_frequency; + + calc_pll_cs->min_pix_clock_pll_post_divider = + init_data->min_pix_clk_pll_post_divider; + calc_pll_cs->max_pix_clock_pll_post_divider = + init_data->max_pix_clk_pll_post_divider; + calc_pll_cs->min_pll_ref_divider = + init_data->min_pll_ref_divider; + calc_pll_cs->max_pll_ref_divider = + init_data->max_pll_ref_divider; + + if (init_data->num_fract_fb_divider_decimal_point == 0 || + init_data->num_fract_fb_divider_decimal_point_precision > + init_data->num_fract_fb_divider_decimal_point) { + dal_logger_write(calc_pll_cs->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "The dec point num or precision is incorrect!"); + return false; + } + if (init_data->num_fract_fb_divider_decimal_point_precision == 0) { + dal_logger_write(calc_pll_cs->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_GPU, + "Incorrect fract feedback divider precision num!"); + return false; + } + + calc_pll_cs->fract_fb_divider_decimal_points_num = + init_data->num_fract_fb_divider_decimal_point; + calc_pll_cs->fract_fb_divider_precision = + init_data->num_fract_fb_divider_decimal_point_precision; + calc_pll_cs->fract_fb_divider_factor = 1; + for (i = 0; i < calc_pll_cs->fract_fb_divider_decimal_points_num; ++i) + calc_pll_cs->fract_fb_divider_factor *= 10; + + calc_pll_cs->fract_fb_divider_precision_factor = 1; + for ( + i = 0; + i < (calc_pll_cs->fract_fb_divider_decimal_points_num - + calc_pll_cs->fract_fb_divider_precision); + ++i) + calc_pll_cs->fract_fb_divider_precision_factor *= 10; + + return true; +} + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id id, + const struct dce110_clk_src_reg_offsets *reg_offsets) +{ + struct firmware_info fw_info = { { 0 } }; +/* structure normally used with PLL ranges from ATOMBIOS; DS on by default */ + struct calc_pll_clock_source_init_data calc_pll_cs_init_data = { + bios, + 1, /* minPixelClockPLLPostDivider */ + PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK, + /* maxPixelClockPLLPostDivider*/ + 1,/* minPLLRefDivider*/ + PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/ + 0, +/* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + 0, +/* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, +/*numberOfFractFBDividerDecimalPoints*/ + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, +/*number of decimal point to round off for fractional feedback divider value*/ + ctx + }; +/*structure for HDMI, no SS or SS% <= 0.06% for 27 MHz Ref clock */ + struct calc_pll_clock_source_init_data calc_pll_cs_init_data_hdmi = { + bios, + 1, /* minPixelClockPLLPostDivider */ + PLL_POST_DIV__PLL_POST_DIV_PIXCLK_MASK, + /* maxPixelClockPLLPostDivider*/ + 1,/* minPLLRefDivider*/ + PLL_REF_DIV__PLL_REF_DIV_MASK,/* maxPLLRefDivider*/ + 13500, + /* when 0 use minInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + 27000, + /* when 0 use maxInputPxlClkPLLFrequencyInKHz from firmwareInfo*/ + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, + /*numberOfFractFBDividerDecimalPoints*/ + FRACT_FB_DIVIDER_DEC_POINTS_MAX_NUM, +/*number of decimal point to round off for fractional feedback divider value*/ + ctx + }; + + clk_src->base.ctx = ctx; + clk_src->bios = bios; + clk_src->base.id = id; + clk_src->base.funcs = &dce110_clk_src_funcs; + clk_src->offsets = *reg_offsets; + + if (clk_src->bios->funcs->get_firmware_info( + clk_src->bios, &fw_info) != BP_RESULT_OK) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + clk_src->ext_clk_khz = + fw_info.external_clock_source_frequency_for_dp; + clk_src->ref_freq_khz = fw_info.pll_info.crystal_frequency; + + if (clk_src->base.id == CLOCK_SOURCE_ID_EXTERNAL) + return true; + + /* PLL only from here on */ + ss_info_from_atombios_create(clk_src); + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll, + &calc_pll_cs_init_data)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + if (clk_src->ref_freq_khz == 48000) { + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = 24000; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = 48000; + } else if (clk_src->ref_freq_khz == 100000) { + calc_pll_cs_init_data_hdmi. + min_override_input_pxl_clk_pll_freq_khz = 25000; + calc_pll_cs_init_data_hdmi. + max_override_input_pxl_clk_pll_freq_khz = 50000; + } + + if (!calc_pll_max_vco_construct( + &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + ASSERT_CRITICAL(false); + goto unexpected_failure; + } + + return true; + +unexpected_failure: + return false; +} + + diff --git a/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h new file mode 100644 index 000000000000..4fa82dad271f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dc/dce110/dce110_clock_source.h @@ -0,0 +1,64 @@ +/* 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_CLOCK_SOURCE_DCE110_H__ +#define __DC_CLOCK_SOURCE_DCE110_H__ + +#include "../inc/clock_source.h" + +#define TO_DCE110_CLK_SRC(clk_src)\ + container_of(clk_src, struct dce110_clk_src, base) + +struct dce110_clk_src_reg_offsets { + uint32_t pll_cntl; + uint32_t pixclk_resync_cntl; +}; + +struct dce110_clk_src { + struct clock_source base; + struct dce110_clk_src_reg_offsets offsets; + struct dc_bios *bios; + + struct spread_spectrum_data *dp_ss_params; + uint32_t dp_ss_params_cnt; + struct spread_spectrum_data *hdmi_ss_params; + uint32_t hdmi_ss_params_cnt; + struct spread_spectrum_data *dvi_ss_params; + uint32_t dvi_ss_params_cnt; + + uint32_t ext_clk_khz; + uint32_t ref_freq_khz; + + struct calc_pll_clock_source calc_pll; + struct calc_pll_clock_source calc_pll_hdmi; +}; + +bool dce110_clk_src_construct( + struct dce110_clk_src *clk_src, + struct dc_context *ctx, + struct dc_bios *bios, + enum clock_source_id, + const struct dce110_clk_src_reg_offsets *reg_offsets); + +#endif