linux/drivers/gpu/drm/nouveau/nvkm/subdev/clk/pllnv04.c
<<
>>
Prefs
   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
  28static int
  29getMNP_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
 127static int
 128getMNP_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
 226int
 227nv04_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}
 246