Commit b58e87f04ec53f9debbe934fe9009e5f7d45f21a
1 parent
9ed0a21714
Exists in
smarc_8mm_imx_4.14.98_2.0.0_ga
and in
4 other branches
MLK-21150-4 drm/bridge: sec-dsim: a general way to compute PLL PMS
A fixed PLL PMS setting for attached panel is obviously not enough for any other mipi panel which needs a different PLL output clock frequency, and besides, for the CEA-861 standard display modes, the 'pll_pms' table also can not cover all the modes requirements. So a general way is created to solve this problem which can provide an optimum solution to output a PLL bit clock to match the request frequency in a maximum degree and also satisfy the input clock and intermediate clocks limit according to the PLL specification. Signed-off-by: Fancy Fang <chen.fang@nxp.com> (cherry picked from commit a73fdd5e48fe0df47685cfc197fe66edc1e28405)
Showing 4 changed files with 212 additions and 63 deletions Side-by-side Diff
drivers/gpu/drm/bridge/sec-dsim.c
... | ... | @@ -18,6 +18,8 @@ |
18 | 18 | #include <linux/clk.h> |
19 | 19 | #include <linux/completion.h> |
20 | 20 | #include <linux/delay.h> |
21 | +#include <linux/gcd.h> | |
22 | +#include <linux/log2.h> | |
21 | 23 | #include <linux/module.h> |
22 | 24 | #include <linux/of_graph.h> |
23 | 25 | #include <drm/bridge/sec_mipi_dsim.h> |
... | ... | @@ -273,15 +275,6 @@ |
273 | 275 | #define conn_to_sec_mipi_dsim(conn) \ |
274 | 276 | container_of(conn, struct sec_mipi_dsim, connector) |
275 | 277 | |
276 | -/* DSIM PLL configuration from spec: | |
277 | - * | |
278 | - * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S) | |
279 | - * Fin_pll = Fin / P (6 ~ 12 MHz) | |
280 | - * S: [2:0], M: [12:3], P: [18:13], so | |
281 | - * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33] | |
282 | - * | |
283 | - */ | |
284 | - | |
285 | 278 | /* used for CEA standard modes */ |
286 | 279 | struct dsim_hblank_par { |
287 | 280 | char *name; /* drm display mode name */ |
... | ... | @@ -297,6 +290,7 @@ |
297 | 290 | uint32_t p; |
298 | 291 | uint32_t m; |
299 | 292 | uint32_t s; |
293 | + uint32_t k; | |
300 | 294 | }; |
301 | 295 | |
302 | 296 | struct sec_mipi_dsim { |
... | ... | @@ -388,19 +382,6 @@ |
388 | 382 | { DSIM_HBLANK_PARAM("640x480" , 60, 18, 66, 138, 2), }, |
389 | 383 | }; |
390 | 384 | |
391 | -static const struct dsim_pll_pms pll_pms[] = { | |
392 | - { DSIM_PLL_PMS(891000, 1, 66, 1), }, | |
393 | - { DSIM_PLL_PMS(890112, 1, 66, 1), }, | |
394 | - { DSIM_PLL_PMS(594000, 3, 66, 0), }, | |
395 | - { DSIM_PLL_PMS(593408, 3, 66, 0), }, | |
396 | - { DSIM_PLL_PMS(445500, 1, 66, 2), }, | |
397 | - { DSIM_PLL_PMS(445056, 1, 66, 2), }, | |
398 | - { DSIM_PLL_PMS(324000, 3, 72, 1), }, | |
399 | - { DSIM_PLL_PMS(324324, 3, 72, 1), }, | |
400 | - { DSIM_PLL_PMS(162000, 3, 72, 2), }, | |
401 | - { DSIM_PLL_PMS(162162, 3, 72, 2), }, | |
402 | -}; | |
403 | - | |
404 | 385 | static const struct dsim_hblank_par *sec_mipi_dsim_get_hblank_par(const char *name, |
405 | 386 | int vrefresh, |
406 | 387 | int lanes) |
407 | 388 | |
... | ... | @@ -446,13 +427,16 @@ |
446 | 427 | int ret; |
447 | 428 | uint32_t rate; |
448 | 429 | struct device *dev = dsim->dev; |
430 | + const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata; | |
431 | + const struct sec_mipi_dsim_pll *dpll = pdata->dphy_pll; | |
432 | + const struct sec_mipi_dsim_range *fin_range = &dpll->fin; | |
449 | 433 | |
450 | 434 | ret = of_property_read_u32(dev->of_node, "pref-rate", &rate); |
451 | 435 | if (ret < 0) { |
452 | 436 | dev_dbg(dev, "no valid rate assigned for pref clock\n"); |
453 | 437 | dsim->pref_clk = PHY_REF_CLK; |
454 | 438 | } else { |
455 | - if (unlikely(rate < 6000 || rate > 300000)) { | |
439 | + if (unlikely(rate < fin_range->min || rate > fin_range->max)) { | |
456 | 440 | dev_warn(dev, "pref-rate get is invalid: %uKHz\n", |
457 | 441 | rate); |
458 | 442 | dsim->pref_clk = PHY_REF_CLK; |
... | ... | @@ -493,21 +477,6 @@ |
493 | 477 | return 0; |
494 | 478 | } |
495 | 479 | |
496 | -static const struct dsim_pll_pms *sec_mipi_dsim_get_pms(uint32_t bit_clk) | |
497 | -{ | |
498 | - int i; | |
499 | - const struct dsim_pll_pms *pms; | |
500 | - | |
501 | - for (i = 0; i < ARRAY_SIZE(pll_pms); i++) { | |
502 | - pms = &pll_pms[i]; | |
503 | - | |
504 | - if (bit_clk == pms->bit_clk) | |
505 | - return pms; | |
506 | - } | |
507 | - | |
508 | - return NULL; | |
509 | -} | |
510 | - | |
511 | 480 | static void sec_mipi_dsim_irq_init(struct sec_mipi_dsim *dsim); |
512 | 481 | |
513 | 482 | /* For now, dsim only support one device attached */ |
... | ... | @@ -936,7 +905,7 @@ |
936 | 905 | bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); |
937 | 906 | |
938 | 907 | /* calculate hfp & hbp word counts */ |
939 | - if (dsim->panel || !dsim->hpar) { | |
908 | + if (!dsim->hpar) { | |
940 | 909 | wc = DIV_ROUND_UP(vmode->hfront_porch * (bpp >> 3), |
941 | 910 | dsim->lanes); |
942 | 911 | hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ? |
... | ... | @@ -956,7 +925,7 @@ |
956 | 925 | dsim_write(dsim, mhporch, DSIM_MHPORCH); |
957 | 926 | |
958 | 927 | /* calculate hsa word counts */ |
959 | - if (dsim->panel || !dsim->hpar) { | |
928 | + if (!dsim->hpar) { | |
960 | 929 | wc = DIV_ROUND_UP(vmode->hsync_len * (bpp >> 3), |
961 | 930 | dsim->lanes); |
962 | 931 | hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ? |
963 | 932 | |
964 | 933 | |
... | ... | @@ -1170,15 +1139,131 @@ |
1170 | 1139 | dsim_write(dsim, mdresol, DSIM_MDRESOL); |
1171 | 1140 | } |
1172 | 1141 | |
1142 | +struct dsim_pll_pms *sec_mipi_dsim_calc_pmsk(struct sec_mipi_dsim *dsim) | |
1143 | +{ | |
1144 | + uint32_t p, m, s; | |
1145 | + uint32_t best_p, best_m, best_s; | |
1146 | + uint32_t fin, fout; | |
1147 | + uint32_t s_pow_2, raw_s; | |
1148 | + uint64_t mfin, pfvco, pfout, psfout; | |
1149 | + uint32_t delta, best_delta = ~0U; | |
1150 | + struct dsim_pll_pms *pll_pms; | |
1151 | + struct device *dev = dsim->dev; | |
1152 | + const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata; | |
1153 | + struct sec_mipi_dsim_pll dpll = *pdata->dphy_pll; | |
1154 | + struct sec_mipi_dsim_range *prange = &dpll.p; | |
1155 | + struct sec_mipi_dsim_range *mrange = &dpll.m; | |
1156 | + struct sec_mipi_dsim_range *srange = &dpll.s; | |
1157 | + struct sec_mipi_dsim_range *krange = &dpll.k; | |
1158 | + struct sec_mipi_dsim_range *fvco_range = &dpll.fvco; | |
1159 | + struct sec_mipi_dsim_range *fpref_range = &dpll.fpref; | |
1160 | + struct sec_mipi_dsim_range pr_new = *prange; | |
1161 | + struct sec_mipi_dsim_range sr_new = *srange; | |
1162 | + | |
1163 | + pll_pms = devm_kzalloc(dev, sizeof(*pll_pms), GFP_KERNEL); | |
1164 | + if (!pll_pms) { | |
1165 | + dev_err(dev, "Unable to allocate 'pll_pms'\n"); | |
1166 | + return ERR_PTR(-ENOMEM); | |
1167 | + } | |
1168 | + | |
1169 | + fout = dsim->bit_clk; | |
1170 | + fin = dsim->pref_clk; | |
1171 | + | |
1172 | + /* TODO: ignore 'k' for PMS calculation, | |
1173 | + * only use 'p', 'm' and 's' to generate | |
1174 | + * the requested PLL output clock. | |
1175 | + */ | |
1176 | + krange->min = 0; | |
1177 | + krange->max = 0; | |
1178 | + | |
1179 | + /* narrow 'p' range via 'Fpref' limitation: | |
1180 | + * Fpref : [2MHz ~ 30MHz] (Fpref = Fin / p) | |
1181 | + */ | |
1182 | + prange->min = max(prange->min, DIV_ROUND_UP(fin, fpref_range->max)); | |
1183 | + prange->max = min(prange->max, fin / fpref_range->min); | |
1184 | + | |
1185 | + /* narrow 'm' range via 'Fvco' limitation: | |
1186 | + * Fvco: [1050MHz ~ 2100MHz] (Fvco = ((m + k / 65536) * Fin) / p) | |
1187 | + * So, m = Fvco * p / Fin and Fvco > Fin; | |
1188 | + */ | |
1189 | + pfvco = fvco_range->min * prange->min; | |
1190 | + mrange->min = max_t(uint32_t, mrange->min, | |
1191 | + DIV_ROUND_UP_ULL(pfvco, fin)); | |
1192 | + pfvco = fvco_range->max * prange->max; | |
1193 | + mrange->max = min_t(uint32_t, mrange->max, | |
1194 | + DIV_ROUND_UP_ULL(pfvco, fin)); | |
1195 | + | |
1196 | + dev_dbg(dev, "p: min = %u, max = %u, " | |
1197 | + "m: min = %u, max = %u, " | |
1198 | + "s: min = %u, max = %u\n", | |
1199 | + prange->min, prange->max, mrange->min, | |
1200 | + mrange->max, srange->min, srange->max); | |
1201 | + | |
1202 | + /* first determine 'm', then can determine 'p', last determine 's' */ | |
1203 | + for (m = mrange->min; m <= mrange->max; m++) { | |
1204 | + /* p = m * Fin / Fvco */ | |
1205 | + mfin = m * fin; | |
1206 | + pr_new.min = max_t(uint32_t, prange->min, | |
1207 | + DIV_ROUND_UP_ULL(mfin, fvco_range->max)); | |
1208 | + pr_new.max = min_t(uint32_t, prange->max, | |
1209 | + (mfin / fvco_range->min)); | |
1210 | + | |
1211 | + if (pr_new.max < pr_new.min || pr_new.min < prange->min) | |
1212 | + continue; | |
1213 | + | |
1214 | + for (p = pr_new.min; p <= pr_new.max; p++) { | |
1215 | + /* s = order_pow_of_two((m * Fin) / (p * Fout)) */ | |
1216 | + pfout = p * fout; | |
1217 | + raw_s = DIV_ROUND_CLOSEST_ULL(mfin, pfout); | |
1218 | + | |
1219 | + s_pow_2 = rounddown_pow_of_two(raw_s); | |
1220 | + sr_new.min = max_t(uint32_t, srange->min, | |
1221 | + order_base_2(s_pow_2)); | |
1222 | + | |
1223 | + s_pow_2 = roundup_pow_of_two(DIV_ROUND_CLOSEST_ULL(mfin, pfout)); | |
1224 | + sr_new.max = min_t(uint32_t, srange->max, | |
1225 | + order_base_2(s_pow_2)); | |
1226 | + | |
1227 | + if (sr_new.max < sr_new.min || sr_new.min < srange->min) | |
1228 | + continue; | |
1229 | + | |
1230 | + for (s = sr_new.min; s <= sr_new.max; s++) { | |
1231 | + /* fout = m * Fin / (p * 2^s) */ | |
1232 | + psfout = pfout * (1 << s); | |
1233 | + delta = abs(psfout - mfin); | |
1234 | + if (delta < best_delta) { | |
1235 | + best_p = p; | |
1236 | + best_m = m; | |
1237 | + best_s = s; | |
1238 | + best_delta = delta; | |
1239 | + } | |
1240 | + } | |
1241 | + } | |
1242 | + } | |
1243 | + | |
1244 | + if (best_delta == ~0U) | |
1245 | + return ERR_PTR(-EINVAL); | |
1246 | + | |
1247 | + pll_pms->p = best_p; | |
1248 | + pll_pms->m = best_m; | |
1249 | + pll_pms->s = best_s; | |
1250 | + | |
1251 | + dev_dbg(dev, "fout = %u, fin = %u, m = %u, " | |
1252 | + "p = %u, s = %u, best_delta = %u\n", | |
1253 | + fout, fin, pll_pms->m, pll_pms->p, pll_pms->s, best_delta); | |
1254 | + | |
1255 | + return pll_pms; | |
1256 | +} | |
1257 | + | |
1173 | 1258 | int sec_mipi_dsim_check_pll_out(void *driver_private, |
1174 | 1259 | const struct drm_display_mode *mode) |
1175 | 1260 | { |
1176 | 1261 | int bpp; |
1177 | - uint32_t pix_clk, bit_clk, ref_clk; | |
1262 | + uint32_t pix_clk, bit_clk; | |
1178 | 1263 | struct sec_mipi_dsim *dsim = driver_private; |
1179 | 1264 | const struct sec_mipi_dsim_plat_data *pdata = dsim->pdata; |
1180 | 1265 | const struct dsim_hblank_par *hpar; |
1181 | - const struct dsim_pll_pms *pms; | |
1266 | + const struct dsim_pll_pms *pmsk; | |
1182 | 1267 | |
1183 | 1268 | bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); |
1184 | 1269 | if (bpp < 0) |
1185 | 1270 | |
1186 | 1271 | |
1187 | 1272 | |
1188 | 1273 | |
... | ... | @@ -1195,32 +1280,27 @@ |
1195 | 1280 | |
1196 | 1281 | dsim->pix_clk = pix_clk; |
1197 | 1282 | dsim->bit_clk = bit_clk; |
1198 | - | |
1199 | - dsim->pms = 0x4210; | |
1200 | 1283 | dsim->hpar = NULL; |
1201 | - if (dsim->panel) | |
1202 | - return 0; | |
1203 | 1284 | |
1285 | + pmsk = sec_mipi_dsim_calc_pmsk(dsim); | |
1286 | + if (IS_ERR(pmsk)) { | |
1287 | + dev_err(dsim->dev, | |
1288 | + "failed to get pmsk for: fin = %u, fout = %u\n", | |
1289 | + dsim->pref_clk, dsim->bit_clk); | |
1290 | + return -EINVAL; | |
1291 | + } | |
1292 | + | |
1293 | + dsim->pms = PLLCTRL_SET_P(pmsk->p) | | |
1294 | + PLLCTRL_SET_M(pmsk->m) | | |
1295 | + PLLCTRL_SET_S(pmsk->s); | |
1296 | + | |
1204 | 1297 | if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { |
1205 | 1298 | hpar = sec_mipi_dsim_get_hblank_par(mode->name, |
1206 | 1299 | mode->vrefresh, |
1207 | 1300 | dsim->lanes); |
1208 | - if (!hpar) | |
1209 | - return -EINVAL; | |
1210 | 1301 | dsim->hpar = hpar; |
1211 | - | |
1212 | - pms = sec_mipi_dsim_get_pms(dsim->bit_clk); | |
1213 | - if (WARN_ON(!pms)) | |
1214 | - return -EINVAL; | |
1215 | - | |
1216 | - ref_clk = PHY_REF_CLK; | |
1217 | - /* TODO: add PMS calculate and check | |
1218 | - * Only support '1080p@60Hz' for now, | |
1219 | - * add other modes support later | |
1220 | - */ | |
1221 | - dsim->pms = PLLCTRL_SET_P(pms->p) | | |
1222 | - PLLCTRL_SET_M(pms->m) | | |
1223 | - PLLCTRL_SET_S(pms->s); | |
1302 | + if (!hpar) | |
1303 | + dev_dbg(dsim->dev, "no pre-exist hpar can be used\n"); | |
1224 | 1304 | } |
1225 | 1305 | |
1226 | 1306 | return 0; |
drivers/gpu/drm/imx/sec_mipi_dsim-imx.c
1 | 1 | /* |
2 | 2 | * Samsung MIPI DSI Host Controller on IMX |
3 | 3 | * |
4 | - * Copyright 2018 NXP | |
4 | + * Copyright 2018-2019 NXP | |
5 | 5 | * |
6 | 6 | * This program is free software; you can redistribute it and/or modify |
7 | 7 | * it under the terms of the GNU General Public License as published by |
... | ... | @@ -32,6 +32,7 @@ |
32 | 32 | |
33 | 33 | #include "imx-drm.h" |
34 | 34 | #include "sec_mipi_dphy_ln14lpp.h" |
35 | +#include "sec_mipi_pll_1432x.h" | |
35 | 36 | |
36 | 37 | #define DRIVER_NAME "imx_sec_dsim_drv" |
37 | 38 | |
... | ... | @@ -197,6 +198,7 @@ |
197 | 198 | .version = 0x1060200, |
198 | 199 | .max_data_lanes = 4, |
199 | 200 | .max_data_rate = 1500000000ULL, |
201 | + .dphy_pll = &pll_1432x, | |
200 | 202 | .dphy_timing = dphy_timing_ln14lpp_v1p2, |
201 | 203 | .num_dphy_timing = ARRAY_SIZE(dphy_timing_ln14lpp_v1p2), |
202 | 204 | .dphy_timing_cmp = dphy_timing_default_cmp, |
drivers/gpu/drm/imx/sec_mipi_pll_1432x.h
1 | +/* | |
2 | + * Samsung MIPI DSIM PLL_1432X | |
3 | + * | |
4 | + * Copyright 2019 NXP | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + */ | |
16 | + | |
17 | +#ifndef __SEC_DSIM_PLL_1432X_H__ | |
18 | +#define __SEC_DSIM_PLL_1432X_H__ | |
19 | + | |
20 | +#include <drm/bridge/sec_mipi_dsim.h> | |
21 | +/* | |
22 | + * DSIM PLL_1432X setting guide from spec: | |
23 | + * | |
24 | + * Fout(bitclk) = ((m + k / 65536) * Fin) / (p * 2^s), and | |
25 | + * p = P[5:0], m = M[9:0], s = S[2:0], k = K[15:0]; | |
26 | + * | |
27 | + * Fpref = Fin / p | |
28 | + * Fin: [6MHz ~ 300MHz], Fpref: [2MHz ~ 30MHz] | |
29 | + * | |
30 | + * Fvco = ((m + k / 65536) * Fin) / p | |
31 | + * Fvco: [1050MHz ~ 2100MHz] | |
32 | + * | |
33 | + * 1 <= P[5:0] <= 63, 64 <= M[9:0] <= 1023, | |
34 | + * 0 <= S[2:0] <= 5, -32768 <= K[15:0] <= 32767 | |
35 | + * | |
36 | + */ | |
37 | + | |
38 | +const struct sec_mipi_dsim_pll pll_1432x = { | |
39 | + .p = { .min = 1, .max = 63, }, | |
40 | + .m = { .min = 64, .max = 1023, }, | |
41 | + .s = { .min = 0, .max = 5, }, | |
42 | + .k = { .min = 0, .max = 32768, }, /* abs(k) */ | |
43 | + .fin = { .min = 6000, .max = 300000, }, /* in KHz */ | |
44 | + .fpref = { .min = 2000, .max = 30000, }, /* in KHz */ | |
45 | + .fvco = { .min = 1050000, .max = 2100000, }, /* in KHz */ | |
46 | +}; | |
47 | + | |
48 | +#endif |
include/drm/bridge/sec_mipi_dsim.h
1 | 1 | /* |
2 | - * Copyright 2018 NXP | |
2 | + * Copyright 2018-2019 NXP | |
3 | 3 | * |
4 | 4 | * This program is free software; you can redistribute it and/or modify |
5 | 5 | * it under the terms of the GNU General Public License as published by |
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | #include <linux/bsearch.h> |
20 | 20 | |
21 | 21 | struct sec_mipi_dsim_dphy_timing; |
22 | +struct sec_mipi_dsim_pll; | |
22 | 23 | |
23 | 24 | struct sec_mipi_dsim_plat_data { |
24 | 25 | uint32_t version; |
25 | 26 | |
... | ... | @@ -26,9 +27,26 @@ |
26 | 27 | uint64_t max_data_rate; |
27 | 28 | const struct sec_mipi_dsim_dphy_timing *dphy_timing; |
28 | 29 | uint32_t num_dphy_timing; |
30 | + const struct sec_mipi_dsim_pll *dphy_pll; | |
29 | 31 | int (*dphy_timing_cmp)(const void *key, const void *elt); |
30 | 32 | enum drm_mode_status (*mode_valid)(struct drm_connector *connector, |
31 | 33 | struct drm_display_mode *mode); |
34 | +}; | |
35 | + | |
36 | +/* DPHY PLL structure */ | |
37 | +struct sec_mipi_dsim_range { | |
38 | + uint32_t min; | |
39 | + uint32_t max; | |
40 | +}; | |
41 | + | |
42 | +struct sec_mipi_dsim_pll { | |
43 | + struct sec_mipi_dsim_range p; | |
44 | + struct sec_mipi_dsim_range m; | |
45 | + struct sec_mipi_dsim_range s; | |
46 | + struct sec_mipi_dsim_range k; | |
47 | + struct sec_mipi_dsim_range fin; | |
48 | + struct sec_mipi_dsim_range fpref; | |
49 | + struct sec_mipi_dsim_range fvco; | |
32 | 50 | }; |
33 | 51 | |
34 | 52 | /* DPHY timings structure */ |