amdgpu_pll.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. #include <drm/drmP.h>
  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. * amdgpu_pll_reduce_ratio - fractional number reduction
  33. *
  34. * @nom: nominator
  35. * @den: denominator
  36. * @nom_min: minimum value for nominator
  37. * @den_min: minimum value for denominator
  38. *
  39. * Find the greatest common divisor and apply it on both nominator and
  40. * denominator, but make nominator and denominator are at least as large
  41. * as their minimum values.
  42. */
  43. static void amdgpu_pll_reduce_ratio(unsigned *nom, unsigned *den,
  44. unsigned nom_min, unsigned den_min)
  45. {
  46. unsigned tmp;
  47. /* reduce the numbers to a simpler ratio */
  48. tmp = gcd(*nom, *den);
  49. *nom /= tmp;
  50. *den /= tmp;
  51. /* make sure nominator is large enough */
  52. if (*nom < nom_min) {
  53. tmp = DIV_ROUND_UP(nom_min, *nom);
  54. *nom *= tmp;
  55. *den *= tmp;
  56. }
  57. /* make sure the denominator is large enough */
  58. if (*den < den_min) {
  59. tmp = DIV_ROUND_UP(den_min, *den);
  60. *nom *= tmp;
  61. *den *= tmp;
  62. }
  63. }
  64. /**
  65. * amdgpu_pll_get_fb_ref_div - feedback and ref divider calculation
  66. *
  67. * @nom: nominator
  68. * @den: denominator
  69. * @post_div: post divider
  70. * @fb_div_max: feedback divider maximum
  71. * @ref_div_max: reference divider maximum
  72. * @fb_div: resulting feedback divider
  73. * @ref_div: resulting reference divider
  74. *
  75. * Calculate feedback and reference divider for a given post divider. Makes
  76. * sure we stay within the limits.
  77. */
  78. static void amdgpu_pll_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div,
  79. unsigned fb_div_max, unsigned ref_div_max,
  80. unsigned *fb_div, unsigned *ref_div)
  81. {
  82. /* limit reference * post divider to a maximum */
  83. ref_div_max = min(128 / post_div, ref_div_max);
  84. /* get matching reference and feedback divider */
  85. *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max);
  86. *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den);
  87. /* limit fb divider to its maximum */
  88. if (*fb_div > fb_div_max) {
  89. *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div);
  90. *fb_div = fb_div_max;
  91. }
  92. }
  93. /**
  94. * amdgpu_pll_compute - compute PLL paramaters
  95. *
  96. * @pll: information about the PLL
  97. * @dot_clock_p: resulting pixel clock
  98. * fb_div_p: resulting feedback divider
  99. * frac_fb_div_p: fractional part of the feedback divider
  100. * ref_div_p: resulting reference divider
  101. * post_div_p: resulting reference divider
  102. *
  103. * Try to calculate the PLL parameters to generate the given frequency:
  104. * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
  105. */
  106. void amdgpu_pll_compute(struct amdgpu_pll *pll,
  107. u32 freq,
  108. u32 *dot_clock_p,
  109. u32 *fb_div_p,
  110. u32 *frac_fb_div_p,
  111. u32 *ref_div_p,
  112. u32 *post_div_p)
  113. {
  114. unsigned target_clock = pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV ?
  115. freq : freq / 10;
  116. unsigned fb_div_min, fb_div_max, fb_div;
  117. unsigned post_div_min, post_div_max, post_div;
  118. unsigned ref_div_min, ref_div_max, ref_div;
  119. unsigned post_div_best, diff_best;
  120. unsigned nom, den;
  121. /* determine allowed feedback divider range */
  122. fb_div_min = pll->min_feedback_div;
  123. fb_div_max = pll->max_feedback_div;
  124. if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
  125. fb_div_min *= 10;
  126. fb_div_max *= 10;
  127. }
  128. /* determine allowed ref divider range */
  129. if (pll->flags & AMDGPU_PLL_USE_REF_DIV)
  130. ref_div_min = pll->reference_div;
  131. else
  132. ref_div_min = pll->min_ref_div;
  133. if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV &&
  134. pll->flags & AMDGPU_PLL_USE_REF_DIV)
  135. ref_div_max = pll->reference_div;
  136. else
  137. ref_div_max = pll->max_ref_div;
  138. /* determine allowed post divider range */
  139. if (pll->flags & AMDGPU_PLL_USE_POST_DIV) {
  140. post_div_min = pll->post_div;
  141. post_div_max = pll->post_div;
  142. } else {
  143. unsigned vco_min, vco_max;
  144. if (pll->flags & AMDGPU_PLL_IS_LCD) {
  145. vco_min = pll->lcd_pll_out_min;
  146. vco_max = pll->lcd_pll_out_max;
  147. } else {
  148. vco_min = pll->pll_out_min;
  149. vco_max = pll->pll_out_max;
  150. }
  151. if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
  152. vco_min *= 10;
  153. vco_max *= 10;
  154. }
  155. post_div_min = vco_min / target_clock;
  156. if ((target_clock * post_div_min) < vco_min)
  157. ++post_div_min;
  158. if (post_div_min < pll->min_post_div)
  159. post_div_min = pll->min_post_div;
  160. post_div_max = vco_max / target_clock;
  161. if ((target_clock * post_div_max) > vco_max)
  162. --post_div_max;
  163. if (post_div_max > pll->max_post_div)
  164. post_div_max = pll->max_post_div;
  165. }
  166. /* represent the searched ratio as fractional number */
  167. nom = target_clock;
  168. den = pll->reference_freq;
  169. /* reduce the numbers to a simpler ratio */
  170. amdgpu_pll_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
  171. /* now search for a post divider */
  172. if (pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP)
  173. post_div_best = post_div_min;
  174. else
  175. post_div_best = post_div_max;
  176. diff_best = ~0;
  177. for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
  178. unsigned diff;
  179. amdgpu_pll_get_fb_ref_div(nom, den, post_div, fb_div_max,
  180. ref_div_max, &fb_div, &ref_div);
  181. diff = abs(target_clock - (pll->reference_freq * fb_div) /
  182. (ref_div * post_div));
  183. if (diff < diff_best || (diff == diff_best &&
  184. !(pll->flags & AMDGPU_PLL_PREFER_MINM_OVER_MAXP))) {
  185. post_div_best = post_div;
  186. diff_best = diff;
  187. }
  188. }
  189. post_div = post_div_best;
  190. /* get the feedback and reference divider for the optimal value */
  191. amdgpu_pll_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max,
  192. &fb_div, &ref_div);
  193. /* reduce the numbers to a simpler ratio once more */
  194. /* this also makes sure that the reference divider is large enough */
  195. amdgpu_pll_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
  196. /* avoid high jitter with small fractional dividers */
  197. if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV && (fb_div % 10)) {
  198. fb_div_min = max(fb_div_min, (9 - (fb_div % 10)) * 20 + 60);
  199. if (fb_div < fb_div_min) {
  200. unsigned tmp = DIV_ROUND_UP(fb_div_min, fb_div);
  201. fb_div *= tmp;
  202. ref_div *= tmp;
  203. }
  204. }
  205. /* and finally save the result */
  206. if (pll->flags & AMDGPU_PLL_USE_FRAC_FB_DIV) {
  207. *fb_div_p = fb_div / 10;
  208. *frac_fb_div_p = fb_div % 10;
  209. } else {
  210. *fb_div_p = fb_div;
  211. *frac_fb_div_p = 0;
  212. }
  213. *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
  214. (pll->reference_freq * *frac_fb_div_p)) /
  215. (ref_div * post_div * 10);
  216. *ref_div_p = ref_div;
  217. *post_div_p = post_div;
  218. DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
  219. freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p,
  220. ref_div, post_div);
  221. }
  222. /**
  223. * amdgpu_pll_get_use_mask - look up a mask of which pplls are in use
  224. *
  225. * @crtc: drm crtc
  226. *
  227. * Returns the mask of which PPLLs (Pixel PLLs) are in use.
  228. */
  229. u32 amdgpu_pll_get_use_mask(struct drm_crtc *crtc)
  230. {
  231. struct drm_device *dev = crtc->dev;
  232. struct drm_crtc *test_crtc;
  233. struct amdgpu_crtc *test_amdgpu_crtc;
  234. u32 pll_in_use = 0;
  235. list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
  236. if (crtc == test_crtc)
  237. continue;
  238. test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
  239. if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
  240. pll_in_use |= (1 << test_amdgpu_crtc->pll_id);
  241. }
  242. return pll_in_use;
  243. }
  244. /**
  245. * amdgpu_pll_get_shared_dp_ppll - return the PPLL used by another crtc for DP
  246. *
  247. * @crtc: drm crtc
  248. *
  249. * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
  250. * also in DP mode. For DP, a single PPLL can be used for all DP
  251. * crtcs/encoders.
  252. */
  253. int amdgpu_pll_get_shared_dp_ppll(struct drm_crtc *crtc)
  254. {
  255. struct drm_device *dev = crtc->dev;
  256. struct drm_crtc *test_crtc;
  257. struct amdgpu_crtc *test_amdgpu_crtc;
  258. list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
  259. if (crtc == test_crtc)
  260. continue;
  261. test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
  262. if (test_amdgpu_crtc->encoder &&
  263. ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
  264. /* for DP use the same PLL for all */
  265. if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
  266. return test_amdgpu_crtc->pll_id;
  267. }
  268. }
  269. return ATOM_PPLL_INVALID;
  270. }
  271. /**
  272. * amdgpu_pll_get_shared_nondp_ppll - return the PPLL used by another non-DP crtc
  273. *
  274. * @crtc: drm crtc
  275. * @encoder: drm encoder
  276. *
  277. * Returns the PPLL (Pixel PLL) used by another non-DP crtc/encoder which can
  278. * be shared (i.e., same clock).
  279. */
  280. int amdgpu_pll_get_shared_nondp_ppll(struct drm_crtc *crtc)
  281. {
  282. struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
  283. struct drm_device *dev = crtc->dev;
  284. struct drm_crtc *test_crtc;
  285. struct amdgpu_crtc *test_amdgpu_crtc;
  286. u32 adjusted_clock, test_adjusted_clock;
  287. adjusted_clock = amdgpu_crtc->adjusted_clock;
  288. if (adjusted_clock == 0)
  289. return ATOM_PPLL_INVALID;
  290. list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
  291. if (crtc == test_crtc)
  292. continue;
  293. test_amdgpu_crtc = to_amdgpu_crtc(test_crtc);
  294. if (test_amdgpu_crtc->encoder &&
  295. !ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(test_amdgpu_crtc->encoder))) {
  296. /* check if we are already driving this connector with another crtc */
  297. if (test_amdgpu_crtc->connector == amdgpu_crtc->connector) {
  298. /* if we are, return that pll */
  299. if (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID)
  300. return test_amdgpu_crtc->pll_id;
  301. }
  302. /* for non-DP check the clock */
  303. test_adjusted_clock = test_amdgpu_crtc->adjusted_clock;
  304. if ((crtc->mode.clock == test_crtc->mode.clock) &&
  305. (adjusted_clock == test_adjusted_clock) &&
  306. (amdgpu_crtc->ss_enabled == test_amdgpu_crtc->ss_enabled) &&
  307. (test_amdgpu_crtc->pll_id != ATOM_PPLL_INVALID))
  308. return test_amdgpu_crtc->pll_id;
  309. }
  310. }
  311. return ATOM_PPLL_INVALID;
  312. }