LCOV - code coverage report
Current view: top level - drivers/gpu/drm/amd/amdgpu - amdgpu_pll.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 131 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 6 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2014 Advanced Micro Devices, Inc.
       3             :  *
       4             :  * Permission is hereby granted, free of charge, to any person obtaining a
       5             :  * copy of this software and associated documentation files (the "Software"),
       6             :  * to deal in the Software without restriction, including without limitation
       7             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
       8             :  * and/or sell copies of the Software, and to permit persons to whom the
       9             :  * Software is furnished to do so, subject to the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included in
      12             :  * all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      17             :  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
      18             :  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      19             :  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      20             :  * OTHER DEALINGS IN THE SOFTWARE.
      21             :  *
      22             :  */
      23             : 
      24             : #include <drm/amdgpu_drm.h>
      25             : #include "amdgpu.h"
      26             : #include "atom.h"
      27             : #include "atombios_encoders.h"
      28             : #include "amdgpu_pll.h"
      29             : #include <asm/div64.h>
      30             : #include <linux/gcd.h>
      31             : 
      32             : /**
      33             :  * amdgpu_pll_reduce_ratio - fractional number reduction
      34             :  *
      35             :  * @nom: nominator
      36             :  * @den: denominator
      37             :  * @nom_min: minimum value for nominator
      38             :  * @den_min: minimum value for denominator
      39             :  *
      40             :  * Find the greatest common divisor and apply it on both nominator and
      41             :  * denominator, but make nominator and denominator are at least as large
      42             :  * as their minimum values.
      43             :  */
      44           0 : static void amdgpu_pll_reduce_ratio(unsigned *nom, unsigned *den,
      45             :                                     unsigned nom_min, unsigned den_min)
      46             : {
      47             :         unsigned tmp;
      48             : 
      49             :         /* reduce the numbers to a simpler ratio */
      50           0 :         tmp = gcd(*nom, *den);
      51           0 :         *nom /= tmp;
      52           0 :         *den /= tmp;
      53             : 
      54             :         /* make sure nominator is large enough */
      55           0 :         if (*nom < nom_min) {
      56           0 :                 tmp = DIV_ROUND_UP(nom_min, *nom);
      57           0 :                 *nom *= tmp;
      58           0 :                 *den *= tmp;
      59             :         }
      60             : 
      61             :         /* make sure the denominator is large enough */
      62           0 :         if (*den < den_min) {
      63           0 :                 tmp = DIV_ROUND_UP(den_min, *den);
      64           0 :                 *nom *= tmp;
      65           0 :                 *den *= tmp;
      66             :         }
      67           0 : }
      68             : 
      69             : /**
      70             :  * amdgpu_pll_get_fb_ref_div - feedback and ref divider calculation
      71             :  *
      72             :  * @adev: amdgpu_device pointer
      73             :  * @nom: nominator
      74             :  * @den: denominator
      75             :  * @post_div: post divider
      76             :  * @fb_div_max: feedback divider maximum
      77             :  * @ref_div_max: reference divider maximum
      78             :  * @fb_div: resulting feedback divider
      79             :  * @ref_div: resulting reference divider
      80             :  *
      81             :  * Calculate feedback and reference divider for a given post divider. Makes
      82             :  * sure we stay within the limits.
      83             :  */
      84           0 : static void amdgpu_pll_get_fb_ref_div(struct amdgpu_device *adev, unsigned int nom,
      85             :                                       unsigned int den, unsigned int post_div,
      86             :                                       unsigned int fb_div_max, unsigned int ref_div_max,
      87             :                                       unsigned int *fb_div, unsigned int *ref_div)
      88             : {
      89             : 
      90             :         /* limit reference * post divider to a maximum */
      91           0 :         if (adev->family == AMDGPU_FAMILY_SI)
      92           0 :                 ref_div_max = min(100 / post_div, ref_div_max);
      93             :         else
      94           0 :                 ref_div_max = min(128 / post_div, ref_div_max);
      95             : 
      96             :         /* get matching reference and feedback divider */
      97           0 :         *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max);
      98           0 :         *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den);
      99             : 
     100             :         /* limit fb divider to its maximum */
     101           0 :         if (*fb_div > fb_div_max) {
     102           0 :                 *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div);
     103           0 :                 *fb_div = fb_div_max;
     104             :         }
     105           0 : }
     106             : 
     107             : /**
     108             :  * amdgpu_pll_compute - compute PLL paramaters
     109             :  *
     110             :  * @adev: amdgpu_device pointer
     111             :  * @pll: information about the PLL
     112             :  * @freq: requested frequency
     113             :  * @dot_clock_p: resulting pixel clock
     114             :  * @fb_div_p: resulting feedback divider
     115             :  * @frac_fb_div_p: fractional part of the feedback divider
     116             :  * @ref_div_p: resulting reference divider
     117             :  * @post_div_p: resulting reference divider
     118             :  *
     119             :  * Try to calculate the PLL parameters to generate the given frequency:
     120             :  * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
     121             :  */
     122           0 : void amdgpu_pll_compute(struct amdgpu_device *adev,
     123             :                         struct amdgpu_pll *pll,
     124             :                         u32 freq,
     125             :                         u32 *dot_clock_p,
     126             :                         u32 *fb_div_p,
     127             :                         u32 *frac_fb_div_p,
     128             :                         u32 *ref_div_p,
     129             :                         u32 *post_div_p)
     130             : {
     131           0 :         unsigned target_clock = pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV ?
     132           0 :                 freq : freq / 10;
     133             : 
     134             :         unsigned fb_div_min, fb_div_max, fb_div;
     135             :         unsigned post_div_min, post_div_max, post_div;
     136             :         unsigned ref_div_min, ref_div_max, ref_div;
     137             :         unsigned post_div_best, diff_best;
     138             :         unsigned nom, den;
     139             : 
     140             :         /* determine allowed feedback divider range */
     141           0 :         fb_div_min = pll->min_feedback_div;
     142           0 :         fb_div_max = pll->max_feedback_div;
     143             : 
     144           0 :         if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
     145           0 :                 fb_div_min *= 10;
     146           0 :                 fb_div_max *= 10;
     147             :         }
     148             : 
     149             :         /* determine allowed ref divider range */
     150           0 :         if (pll->flags & AMDGPU_PLL_USE_REF_DIV)
     151           0 :                 ref_div_min = pll->reference_div;
     152             :         else
     153           0 :                 ref_div_min = pll->min_ref_div;
     154             : 
     155           0 :         if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV &&
     156             :             pll->flags & AMDGPU_PLL_USE_REF_DIV)
     157           0 :                 ref_div_max = pll->reference_div;
     158             :         else
     159           0 :                 ref_div_max = pll->max_ref_div;
     160             : 
     161             :         /* determine allowed post divider range */
     162           0 :         if (pll->flags & AMDGPU_PLL_USE_POST_DIV) {
     163           0 :                 post_div_min = pll->post_div;
     164           0 :                 post_div_max = pll->post_div;
     165             :         } else {
     166             :                 unsigned vco_min, vco_max;
     167             : 
     168           0 :                 if (pll->flags & AMDGPU_PLL_IS_LCD) {
     169           0 :                         vco_min = pll->lcd_pll_out_min;
     170           0 :                         vco_max = pll->lcd_pll_out_max;
     171             :                 } else {
     172           0 :                         vco_min = pll->pll_out_min;
     173           0 :                         vco_max = pll->pll_out_max;
     174             :                 }
     175             : 
     176           0 :                 if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
     177           0 :                         vco_min *= 10;
     178           0 :                         vco_max *= 10;
     179             :                 }
     180             : 
     181           0 :                 post_div_min = vco_min / target_clock;
     182           0 :                 if ((target_clock * post_div_min) < vco_min)
     183           0 :                         ++post_div_min;
     184           0 :                 if (post_div_min < pll->min_post_div)
     185           0 :                         post_div_min = pll->min_post_div;
     186             : 
     187           0 :                 post_div_max = vco_max / target_clock;
     188           0 :                 if ((target_clock * post_div_max) > vco_max)
     189           0 :                         --post_div_max;
     190           0 :                 if (post_div_max > pll->max_post_div)
     191           0 :                         post_div_max = pll->max_post_div;
     192             :         }
     193             : 
     194             :         /* represent the searched ratio as fractional number */
     195           0 :         nom = target_clock;
     196           0 :         den = pll->reference_freq;
     197             : 
     198             :         /* reduce the numbers to a simpler ratio */
     199           0 :         amdgpu_pll_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
     200             : 
     201             :         /* now search for a post divider */
     202           0 :         if (pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP)
     203             :                 post_div_best = post_div_min;
     204             :         else
     205           0 :                 post_div_best = post_div_max;
     206             :         diff_best = ~0;
     207             : 
     208           0 :         for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
     209             :                 unsigned diff;
     210           0 :                 amdgpu_pll_get_fb_ref_div(adev, nom, den, post_div, fb_div_max,
     211             :                                           ref_div_max, &fb_div, &ref_div);
     212           0 :                 diff = abs(target_clock - (pll->reference_freq * fb_div) /
     213             :                         (ref_div * post_div));
     214             : 
     215           0 :                 if (diff < diff_best || (diff == diff_best &&
     216           0 :                     !(pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP))) {
     217             : 
     218           0 :                         post_div_best = post_div;
     219           0 :                         diff_best = diff;
     220             :                 }
     221             :         }
     222           0 :         post_div = post_div_best;
     223             : 
     224             :         /* get the feedback and reference divider for the optimal value */
     225           0 :         amdgpu_pll_get_fb_ref_div(adev, nom, den, post_div, fb_div_max, ref_div_max,
     226             :                                   &fb_div, &ref_div);
     227             : 
     228             :         /* reduce the numbers to a simpler ratio once more */
     229             :         /* this also makes sure that the reference divider is large enough */
     230           0 :         amdgpu_pll_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
     231             : 
     232             :         /* avoid high jitter with small fractional dividers */
     233           0 :         if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) {
     234           0 :                 fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 60);
     235           0 :                 if (fb_div < fb_div_min) {
     236           0 :                         unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div);
     237           0 :                         fb_div *= tmp;
     238           0 :                         ref_div *= tmp;
     239             :                 }
     240             :         }
     241             : 
     242             :         /* and finally save the result */
     243           0 :         if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
     244           0 :                 *fb_div_p = fb_div / 10;
     245           0 :                 *frac_fb_div_p = fb_div % 10;
     246             :         } else {
     247           0 :                 *fb_div_p = fb_div;
     248           0 :                 *frac_fb_div_p = 0;
     249             :         }
     250             : 
     251           0 :         *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
     252           0 :                         (pll->reference_freq * *frac_fb_div_p)) /
     253           0 :                        (ref_div * post_div * 10);
     254           0 :         *ref_div_p = ref_div;
     255           0 :         *post_div_p = post_div;
     256             : 
     257           0 :         DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
     258             :                       freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p,
     259             :                       ref_div, post_div);
     260           0 : }
     261             : 
     262             : /**
     263             :  * amdgpu_pll_get_use_mask - look up a mask of which pplls are in use
     264             :  *
     265             :  * @crtc: drm crtc
     266             :  *
     267             :  * Returns the mask of which PPLLs (Pixel PLLs) are in use.
     268             :  */
     269           0 : u32 amdgpu_pll_get_use_mask(struct drm_crtc *crtc)
     270             : {
     271           0 :         struct drm_device *dev = crtc->dev;
     272             :         struct drm_crtc *test_crtc;
     273             :         struct amdgpu_crtc *test_amdgpu_crtc;
     274           0 :         u32 pll_in_use = 0;
     275             : 
     276           0 :         list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
     277           0 :                 if (crtc == test_crtc)
     278           0 :                         continue;
     279             : 
     280           0 :                 test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
     281           0 :                 if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
     282           0 :                         pll_in_use |= (1 << test_amdgpu_crtc->pll_id);
     283             :         }
     284           0 :         return pll_in_use;
     285             : }
     286             : 
     287             : /**
     288             :  * amdgpu_pll_get_shared_dp_ppll - return the PPLL used by another crtc for DP
     289             :  *
     290             :  * @crtc: drm crtc
     291             :  *
     292             :  * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
     293             :  * also in DP mode.  For DP, a single PPLL can be used for all DP
     294             :  * crtcs/encoders.
     295             :  */
     296           0 : int amdgpu_pll_get_shared_dp_ppll(struct drm_crtc *crtc)
     297             : {
     298           0 :         struct drm_device *dev = crtc->dev;
     299             :         struct drm_crtc *test_crtc;
     300             :         struct amdgpu_crtc *test_amdgpu_crtc;
     301             : 
     302           0 :         list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
     303           0 :                 if (crtc == test_crtc)
     304           0 :                         continue;
     305           0 :                 test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
     306           0 :                 if (test_amdgpu_crtc->encoder &&
     307           0 :                     ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
     308             :                         /* for DP use the same PLL for all */
     309           0 :                         if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
     310           0 :                                 return test_amdgpu_crtc->pll_id;
     311             :                 }
     312             :         }
     313             :         return ATOM_PPLL_INVALID;
     314             : }
     315             : 
     316             : /**
     317             :  * amdgpu_pll_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc
     318             :  *
     319             :  * @crtc: drm crtc
     320             :  *
     321             :  * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can
     322             :  * be shared (i.e., same clock).
     323             :  */
     324           0 : int amdgpu_pll_get_shared_nondp_ppll(struct drm_crtc *crtc)
     325             : {
     326           0 :         struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
     327           0 :         struct drm_device *dev = crtc->dev;
     328             :         struct drm_crtc *test_crtc;
     329             :         struct amdgpu_crtc *test_amdgpu_crtc;
     330             :         u32 adjusted_clock, test_adjusted_clock;
     331             : 
     332           0 :         adjusted_clock = amdgpu_crtc->adjusted_clock;
     333             : 
     334           0 :         if (adjusted_clock == 0)
     335             :                 return ATOM_PPLL_INVALID;
     336             : 
     337           0 :         list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
     338           0 :                 if (crtc == test_crtc)
     339           0 :                         continue;
     340           0 :                 test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
     341           0 :                 if (test_amdgpu_crtc->encoder &&
     342           0 :                     !ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
     343             :                         /* check if we are already driving this connector with another crtc */
     344           0 :                         if (test_amdgpu_crtc->connector == amdgpu_crtc->connector) {
     345             :                                 /* if we are, return that pll */
     346           0 :                                 if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
     347           0 :                                         return test_amdgpu_crtc->pll_id;
     348             :                         }
     349             :                         /* for non-DP check the clock */
     350           0 :                         test_adjusted_clock = test_amdgpu_crtc->adjusted_clock;
     351           0 :                         if ((crtc->mode.clock == test_crtc->mode.clock) &&
     352           0 :                             (adjusted_clock == test_adjusted_clock) &&
     353           0 :                             (amdgpu_crtc->ss_enabled == test_amdgpu_crtc->ss_enabled) &&
     354           0 :                             (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID))
     355           0 :                                 return test_amdgpu_crtc->pll_id;
     356             :                 }
     357             :         }
     358             :         return ATOM_PPLL_INVALID;
     359             : }

Generated by: LCOV version 1.14