/* * Copyright 2012 Red Hat 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: Ben Skeggs
*/ #include"nv50.h" #include"pll.h" #include"seq.h"
static u32
read_div(struct nv50_clk *clk)
{ struct nvkm_device *device = clk->base.subdev.device; switch (device->chipset) { case 0x50: /* it exists, but only has bit 31, not the dividers.. */ case 0x84: case 0x86: case 0x98: case 0xa0: return nvkm_rd32(device, 0x004700); case 0x92: case 0x94: case 0x96: return nvkm_rd32(device, 0x004800); default: return 0x00000000;
}
}
switch (device->chipset) { case 0x50: case 0xa0: switch (base) { case 0x4020: case 0x4028: id = !!(rsel & 0x00000004); break; case 0x4008: id = !!(rsel & 0x00000008); break; case 0x4030: id = 0; break; default:
nvkm_error(subdev, "ref: bad pll %06x\n", base); return 0;
}
coef = nvkm_rd32(device, 0x00e81c + (id * 0x0c));
ref *= (coef & 0x01000000) ? 2 : 4;
P = (coef & 0x00070000) >> 16;
N = ((coef & 0x0000ff00) >> 8) + 1;
M = ((coef & 0x000000ff) >> 0) + 1; break; case 0x84: case 0x86: case 0x92:
coef = nvkm_rd32(device, 0x00e81c);
P = (coef & 0x00070000) >> 16;
N = (coef & 0x0000ff00) >> 8;
M = (coef & 0x000000ff) >> 0; break; case 0x94: case 0x96: case 0x98:
rsel = nvkm_rd32(device, 0x00c050); switch (base) { case 0x4020: rsel = (rsel & 0x00000003) >> 0; break; case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break; case 0x4028: rsel = (rsel & 0x00001800) >> 11; break; case 0x4030: rsel = 3; break; default:
nvkm_error(subdev, "ref: bad pll %06x\n", base); return 0;
}
switch (rsel) { case 0: id = 1; break; case 1: return nvkm_clk_read(&clk->base, nv_clk_src_crystal); case 2: return nvkm_clk_read(&clk->base, nv_clk_src_href); case 3: id = 0; break;
}
coef = nvkm_rd32(device, 0x00e81c + (id * 0x28));
P = (nvkm_rd32(device, 0x00e824 + (id * 0x28)) >> 16) & 7;
P += (coef & 0x00070000) >> 16;
N = (coef & 0x0000ff00) >> 8;
M = (coef & 0x000000ff) >> 0; break; default:
BUG();
}
/* vdec: avoid modifying xpll until we know exactly how the other * clock domains work, i suspect at least some of them can also be * tied to xpll...
*/ if (vdec) { /* see how close we can get using nvclk as a source */
freq = calc_div(core, vdec, &P1);
/* see how close we can get using xpll/hclk as a source */ if (device->chipset != 0x98)
out = read_pll(clk, 0x004030); else
out = nvkm_clk_read(&clk->base, nv_clk_src_hclkm3d2);
out = calc_div(out, vdec, &P2);
/* dom6: nfi what this is, but we're limited to various combinations * of the host clock frequency
*/ if (dom6) { if (clk_same(dom6, nvkm_clk_read(&clk->base, nv_clk_src_href))) {
mastv |= 0x00000000;
} else if (clk_same(dom6, nvkm_clk_read(&clk->base, nv_clk_src_hclk))) {
mastv |= 0x08000000;
} else {
freq = nvkm_clk_read(&clk->base, nv_clk_src_hclk) * 3;
calc_div(freq, dom6, &P1);
mastv |= 0x0c000000;
divsv |= P1;
}
mastm |= 0x0c000000;
divsm |= 0x00000007;
}
/* vdec/dom6: switch to "safe" clocks temporarily, update dividers * and then switch to target clocks
*/
clk_mask(hwsq, mast, mastm, 0x00000000);
clk_mask(hwsq, divs, divsm, divsv);
clk_mask(hwsq, mast, mastm, mastv);
/* core/shader: disconnect nvclk/sclk from their PLLs (nvclk to dom6, * sclk to hclk) before reprogramming
*/ if (device->chipset < 0x92)
clk_mask(hwsq, mast, 0x001000b0, 0x00100080); else
clk_mask(hwsq, mast, 0x000000b3, 0x00000081);
/* core: for the moment at least, always use nvpll */
freq = calc_pll(clk, 0x4028, core, &N, &M, &P1); if (freq == 0) return -ERANGE;
/* shader: tie to nvclk if possible, otherwise use spll. have to be * very careful that the shader clock is at least twice the core, or * some chipsets will be very unhappy. i expect most or all of these * cases will be handled by tying to nvclk, but it's possible there's * corners
*/ if (P1-- && shader == (core << 1)) {
clk_mask(hwsq, spll[0], 0xc03f0100, (P1 << 19) | (P1 << 16));
clk_mask(hwsq, mast, 0x00100033, 0x00000023);
} else {
freq = calc_pll(clk, 0x4020, shader, &N, &M, &P1); if (freq == 0) return -ERANGE;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.