root/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getMNP_single
  2. getMNP_double
  3. nv04_pll_calc

   1 /*
   2  * Copyright 1993-2003 NVIDIA, Corporation
   3  * Copyright 2007-2009 Stuart Bennett
   4  *
   5  * Permission is hereby granted, free of charge, to any person obtaining a
   6  * copy of this software and associated documentation files (the "Software"),
   7  * to deal in the Software without restriction, including without limitation
   8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   9  * and/or sell copies of the Software, and to permit persons to whom the
  10  * Software is furnished to do so, subject to the following conditions:
  11  *
  12  * The above copyright notice and this permission notice shall be included in
  13  * all copies or substantial portions of the Software.
  14  *
  15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  20  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21  * SOFTWARE.
  22  */
  23 #include "pll.h"
  24 
  25 #include <subdev/bios.h>
  26 #include <subdev/bios/pll.h>
  27 
  28 static int
  29 getMNP_single(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
  30               int *pN, int *pM, int *pP)
  31 {
  32         /* Find M, N and P for a single stage PLL
  33          *
  34          * Note that some bioses (NV3x) have lookup tables of precomputed MNP
  35          * values, but we're too lazy to use those atm
  36          *
  37          * "clk" parameter in kHz
  38          * returns calculated clock
  39          */
  40         struct nvkm_bios *bios = subdev->device->bios;
  41         int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq;
  42         int minM = info->vco1.min_m, maxM = info->vco1.max_m;
  43         int minN = info->vco1.min_n, maxN = info->vco1.max_n;
  44         int minU = info->vco1.min_inputfreq;
  45         int maxU = info->vco1.max_inputfreq;
  46         int minP = info->min_p;
  47         int maxP = info->max_p_usable;
  48         int crystal = info->refclk;
  49         int M, N, thisP, P;
  50         int clkP, calcclk;
  51         int delta, bestdelta = INT_MAX;
  52         int bestclk = 0;
  53 
  54         /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
  55         /* possibly correlated with introduction of 27MHz crystal */
  56         if (bios->version.major < 0x60) {
  57                 int cv = bios->version.chip;
  58                 if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
  59                         if (clk > 250000)
  60                                 maxM = 6;
  61                         if (clk > 340000)
  62                                 maxM = 2;
  63                 } else if (cv < 0x40) {
  64                         if (clk > 150000)
  65                                 maxM = 6;
  66                         if (clk > 200000)
  67                                 maxM = 4;
  68                         if (clk > 340000)
  69                                 maxM = 2;
  70                 }
  71         }
  72 
  73         P = 1 << maxP;
  74         if ((clk * P) < minvco) {
  75                 minvco = clk * maxP;
  76                 maxvco = minvco * 2;
  77         }
  78 
  79         if (clk + clk/200 > maxvco)     /* +0.5% */
  80                 maxvco = clk + clk/200;
  81 
  82         /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
  83         for (thisP = minP; thisP <= maxP; thisP++) {
  84                 P = 1 << thisP;
  85                 clkP = clk * P;
  86 
  87                 if (clkP < minvco)
  88                         continue;
  89                 if (clkP > maxvco)
  90                         return bestclk;
  91 
  92                 for (M = minM; M <= maxM; M++) {
  93                         if (crystal/M < minU)
  94                                 return bestclk;
  95                         if (crystal/M > maxU)
  96                                 continue;
  97 
  98                         /* add crystal/2 to round better */
  99                         N = (clkP * M + crystal/2) / crystal;
 100 
 101                         if (N < minN)
 102                                 continue;
 103                         if (N > maxN)
 104                                 break;
 105 
 106                         /* more rounding additions */
 107                         calcclk = ((N * crystal + P/2) / P + M/2) / M;
 108                         delta = abs(calcclk - clk);
 109                         /* we do an exhaustive search rather than terminating
 110                          * on an optimality condition...
 111                          */
 112                         if (delta < bestdelta) {
 113                                 bestdelta = delta;
 114                                 bestclk = calcclk;
 115                                 *pN = N;
 116                                 *pM = M;
 117                                 *pP = thisP;
 118                                 if (delta == 0) /* except this one */
 119                                         return bestclk;
 120                         }
 121                 }
 122         }
 123 
 124         return bestclk;
 125 }
 126 
 127 static int
 128 getMNP_double(struct nvkm_subdev *subdev, struct nvbios_pll *info, int clk,
 129               int *pN1, int *pM1, int *pN2, int *pM2, int *pP)
 130 {
 131         /* Find M, N and P for a two stage PLL
 132          *
 133          * Note that some bioses (NV30+) have lookup tables of precomputed MNP
 134          * values, but we're too lazy to use those atm
 135          *
 136          * "clk" parameter in kHz
 137          * returns calculated clock
 138          */
 139         int chip_version = subdev->device->bios->version.chip;
 140         int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq;
 141         int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq;
 142         int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq;
 143         int maxU1 = info->vco1.max_inputfreq, maxU2 = info->vco2.max_inputfreq;
 144         int minM1 = info->vco1.min_m, maxM1 = info->vco1.max_m;
 145         int minN1 = info->vco1.min_n, maxN1 = info->vco1.max_n;
 146         int minM2 = info->vco2.min_m, maxM2 = info->vco2.max_m;
 147         int minN2 = info->vco2.min_n, maxN2 = info->vco2.max_n;
 148         int maxlog2P = info->max_p_usable;
 149         int crystal = info->refclk;
 150         bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
 151         int M1, N1, M2, N2, log2P;
 152         int clkP, calcclk1, calcclk2, calcclkout;
 153         int delta, bestdelta = INT_MAX;
 154         int bestclk = 0;
 155 
 156         int vco2 = (maxvco2 - maxvco2/200) / 2;
 157         for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
 158                 ;
 159         clkP = clk << log2P;
 160 
 161         if (maxvco2 < clk + clk/200)    /* +0.5% */
 162                 maxvco2 = clk + clk/200;
 163 
 164         for (M1 = minM1; M1 <= maxM1; M1++) {
 165                 if (crystal/M1 < minU1)
 166                         return bestclk;
 167                 if (crystal/M1 > maxU1)
 168                         continue;
 169 
 170                 for (N1 = minN1; N1 <= maxN1; N1++) {
 171                         calcclk1 = crystal * N1 / M1;
 172                         if (calcclk1 < minvco1)
 173                                 continue;
 174                         if (calcclk1 > maxvco1)
 175                                 break;
 176 
 177                         for (M2 = minM2; M2 <= maxM2; M2++) {
 178                                 if (calcclk1/M2 < minU2)
 179                                         break;
 180                                 if (calcclk1/M2 > maxU2)
 181                                         continue;
 182 
 183                                 /* add calcclk1/2 to round better */
 184                                 N2 = (clkP * M2 + calcclk1/2) / calcclk1;
 185                                 if (N2 < minN2)
 186                                         continue;
 187                                 if (N2 > maxN2)
 188                                         break;
 189 
 190                                 if (!fixedgain2) {
 191                                         if (chip_version < 0x60)
 192                                                 if (N2/M2 < 4 || N2/M2 > 10)
 193                                                         continue;
 194 
 195                                         calcclk2 = calcclk1 * N2 / M2;
 196                                         if (calcclk2 < minvco2)
 197                                                 break;
 198                                         if (calcclk2 > maxvco2)
 199                                                 continue;
 200                                 } else
 201                                         calcclk2 = calcclk1;
 202 
 203                                 calcclkout = calcclk2 >> log2P;
 204                                 delta = abs(calcclkout - clk);
 205                                 /* we do an exhaustive search rather than terminating
 206                                  * on an optimality condition...
 207                                  */
 208                                 if (delta < bestdelta) {
 209                                         bestdelta = delta;
 210                                         bestclk = calcclkout;
 211                                         *pN1 = N1;
 212                                         *pM1 = M1;
 213                                         *pN2 = N2;
 214                                         *pM2 = M2;
 215                                         *pP = log2P;
 216                                         if (delta == 0) /* except this one */
 217                                                 return bestclk;
 218                                 }
 219                         }
 220                 }
 221         }
 222 
 223         return bestclk;
 224 }
 225 
 226 int
 227 nv04_pll_calc(struct nvkm_subdev *subdev, struct nvbios_pll *info, u32 freq,
 228               int *N1, int *M1, int *N2, int *M2, int *P)
 229 {
 230         int ret;
 231 
 232         if (!info->vco2.max_freq || !N2) {
 233                 ret = getMNP_single(subdev, info, freq, N1, M1, P);
 234                 if (N2) {
 235                         *N2 = 1;
 236                         *M2 = 1;
 237                 }
 238         } else {
 239                 ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P);
 240         }
 241 
 242         if (!ret)
 243                 nvkm_error(subdev, "unable to compute acceptable pll values\n");
 244         return ret;
 245 }

/* [<][>][^][v][top][bottom][index][help] */