diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dsi/phy/dsi_phy.c')
-rw-r--r-- | drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 239 |
1 files changed, 209 insertions, 30 deletions
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index f39386ed75e4..0c2eb9c9a1fc 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | |||
@@ -54,8 +54,10 @@ static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing, | |||
54 | } | 54 | } |
55 | 55 | ||
56 | int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, | 56 | int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, |
57 | const unsigned long bit_rate, const unsigned long esc_rate) | 57 | struct msm_dsi_phy_clk_request *clk_req) |
58 | { | 58 | { |
59 | const unsigned long bit_rate = clk_req->bitclk_rate; | ||
60 | const unsigned long esc_rate = clk_req->escclk_rate; | ||
59 | s32 ui, lpx; | 61 | s32 ui, lpx; |
60 | s32 tmax, tmin; | 62 | s32 tmax, tmin; |
61 | s32 pcnt0 = 10; | 63 | s32 pcnt0 = 10; |
@@ -115,8 +117,8 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, | |||
115 | temp = ((timing->hs_exit >> 1) + 1) * 2 * ui; | 117 | temp = ((timing->hs_exit >> 1) + 1) * 2 * ui; |
116 | temp = 60 * coeff + 52 * ui - 24 * ui - temp; | 118 | temp = 60 * coeff + 52 * ui - 24 * ui - temp; |
117 | tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; | 119 | tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; |
118 | timing->clk_post = linear_inter(tmax, tmin, pcnt2, 0, false); | 120 | timing->shared_timings.clk_post = linear_inter(tmax, tmin, pcnt2, 0, |
119 | 121 | false); | |
120 | tmax = 63; | 122 | tmax = 63; |
121 | temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui; | 123 | temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui; |
122 | temp += ((timing->clk_zero >> 1) + 1) * 2 * ui; | 124 | temp += ((timing->clk_zero >> 1) + 1) * 2 * ui; |
@@ -124,17 +126,21 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, | |||
124 | tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; | 126 | tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; |
125 | if (tmin > tmax) { | 127 | if (tmin > tmax) { |
126 | temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false); | 128 | temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false); |
127 | timing->clk_pre = temp >> 1; | 129 | timing->shared_timings.clk_pre = temp >> 1; |
130 | timing->shared_timings.clk_pre_inc_by_2 = true; | ||
128 | } else { | 131 | } else { |
129 | timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false); | 132 | timing->shared_timings.clk_pre = |
133 | linear_inter(tmax, tmin, pcnt2, 0, false); | ||
134 | timing->shared_timings.clk_pre_inc_by_2 = false; | ||
130 | } | 135 | } |
131 | 136 | ||
132 | timing->ta_go = 3; | 137 | timing->ta_go = 3; |
133 | timing->ta_sure = 0; | 138 | timing->ta_sure = 0; |
134 | timing->ta_get = 4; | 139 | timing->ta_get = 4; |
135 | 140 | ||
136 | DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", | 141 | DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", |
137 | timing->clk_pre, timing->clk_post, timing->clk_zero, | 142 | timing->shared_timings.clk_pre, timing->shared_timings.clk_post, |
143 | timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero, | ||
138 | timing->clk_trail, timing->clk_prepare, timing->hs_exit, | 144 | timing->clk_trail, timing->clk_prepare, timing->hs_exit, |
139 | timing->hs_zero, timing->hs_prepare, timing->hs_trail, | 145 | timing->hs_zero, timing->hs_prepare, timing->hs_trail, |
140 | timing->hs_rqst); | 146 | timing->hs_rqst); |
@@ -142,6 +148,123 @@ int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, | |||
142 | return 0; | 148 | return 0; |
143 | } | 149 | } |
144 | 150 | ||
151 | int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing, | ||
152 | struct msm_dsi_phy_clk_request *clk_req) | ||
153 | { | ||
154 | const unsigned long bit_rate = clk_req->bitclk_rate; | ||
155 | const unsigned long esc_rate = clk_req->escclk_rate; | ||
156 | s32 ui, ui_x8, lpx; | ||
157 | s32 tmax, tmin; | ||
158 | s32 pcnt0 = 50; | ||
159 | s32 pcnt1 = 50; | ||
160 | s32 pcnt2 = 10; | ||
161 | s32 pcnt3 = 30; | ||
162 | s32 pcnt4 = 10; | ||
163 | s32 pcnt5 = 2; | ||
164 | s32 coeff = 1000; /* Precision, should avoid overflow */ | ||
165 | s32 hb_en, hb_en_ckln, pd_ckln, pd; | ||
166 | s32 val, val_ckln; | ||
167 | s32 temp; | ||
168 | |||
169 | if (!bit_rate || !esc_rate) | ||
170 | return -EINVAL; | ||
171 | |||
172 | timing->hs_halfbyte_en = 0; | ||
173 | hb_en = 0; | ||
174 | timing->hs_halfbyte_en_ckln = 0; | ||
175 | hb_en_ckln = 0; | ||
176 | timing->hs_prep_dly_ckln = (bit_rate > 100000000) ? 0 : 3; | ||
177 | pd_ckln = timing->hs_prep_dly_ckln; | ||
178 | timing->hs_prep_dly = (bit_rate > 120000000) ? 0 : 1; | ||
179 | pd = timing->hs_prep_dly; | ||
180 | |||
181 | val = (hb_en << 2) + (pd << 1); | ||
182 | val_ckln = (hb_en_ckln << 2) + (pd_ckln << 1); | ||
183 | |||
184 | ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000); | ||
185 | ui_x8 = ui << 3; | ||
186 | lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000); | ||
187 | |||
188 | temp = S_DIV_ROUND_UP(38 * coeff - val_ckln * ui, ui_x8); | ||
189 | tmin = max_t(s32, temp, 0); | ||
190 | temp = (95 * coeff - val_ckln * ui) / ui_x8; | ||
191 | tmax = max_t(s32, temp, 0); | ||
192 | timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false); | ||
193 | |||
194 | temp = 300 * coeff - ((timing->clk_prepare << 3) + val_ckln) * ui; | ||
195 | tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3; | ||
196 | tmax = (tmin > 255) ? 511 : 255; | ||
197 | timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false); | ||
198 | |||
199 | tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8); | ||
200 | temp = 105 * coeff + 12 * ui - 20 * coeff; | ||
201 | tmax = (temp + 3 * ui) / ui_x8; | ||
202 | timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false); | ||
203 | |||
204 | temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui - val * ui, ui_x8); | ||
205 | tmin = max_t(s32, temp, 0); | ||
206 | temp = (85 * coeff + 6 * ui - val * ui) / ui_x8; | ||
207 | tmax = max_t(s32, temp, 0); | ||
208 | timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false); | ||
209 | |||
210 | temp = 145 * coeff + 10 * ui - ((timing->hs_prepare << 3) + val) * ui; | ||
211 | tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3; | ||
212 | tmax = 255; | ||
213 | timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false); | ||
214 | |||
215 | tmin = DIV_ROUND_UP(60 * coeff + 4 * ui + 3 * ui, ui_x8); | ||
216 | temp = 105 * coeff + 12 * ui - 20 * coeff; | ||
217 | tmax = (temp + 3 * ui) / ui_x8; | ||
218 | timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false); | ||
219 | |||
220 | temp = 50 * coeff + ((hb_en << 2) - 8) * ui; | ||
221 | timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8); | ||
222 | |||
223 | tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1; | ||
224 | tmax = 255; | ||
225 | timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false); | ||
226 | |||
227 | temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui; | ||
228 | timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8); | ||
229 | |||
230 | temp = 60 * coeff + 52 * ui - 43 * ui; | ||
231 | tmin = DIV_ROUND_UP(temp, ui_x8) - 1; | ||
232 | tmax = 63; | ||
233 | timing->shared_timings.clk_post = | ||
234 | linear_inter(tmax, tmin, pcnt2, 0, false); | ||
235 | |||
236 | temp = 8 * ui + ((timing->clk_prepare << 3) + val_ckln) * ui; | ||
237 | temp += (((timing->clk_zero + 3) << 3) + 11 - (pd_ckln << 1)) * ui; | ||
238 | temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) : | ||
239 | (((timing->hs_rqst_ckln << 3) + 8) * ui); | ||
240 | tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1; | ||
241 | tmax = 63; | ||
242 | if (tmin > tmax) { | ||
243 | temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false); | ||
244 | timing->shared_timings.clk_pre = temp >> 1; | ||
245 | timing->shared_timings.clk_pre_inc_by_2 = 1; | ||
246 | } else { | ||
247 | timing->shared_timings.clk_pre = | ||
248 | linear_inter(tmax, tmin, pcnt2, 0, false); | ||
249 | timing->shared_timings.clk_pre_inc_by_2 = 0; | ||
250 | } | ||
251 | |||
252 | timing->ta_go = 3; | ||
253 | timing->ta_sure = 0; | ||
254 | timing->ta_get = 4; | ||
255 | |||
256 | DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", | ||
257 | timing->shared_timings.clk_pre, timing->shared_timings.clk_post, | ||
258 | timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero, | ||
259 | timing->clk_trail, timing->clk_prepare, timing->hs_exit, | ||
260 | timing->hs_zero, timing->hs_prepare, timing->hs_trail, | ||
261 | timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en, | ||
262 | timing->hs_halfbyte_en_ckln, timing->hs_prep_dly, | ||
263 | timing->hs_prep_dly_ckln); | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
145 | void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, | 268 | void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, |
146 | u32 bit_mask) | 269 | u32 bit_mask) |
147 | { | 270 | { |
@@ -268,6 +391,10 @@ static const struct of_device_id dsi_phy_dt_match[] = { | |||
268 | { .compatible = "qcom,dsi-phy-28nm-8960", | 391 | { .compatible = "qcom,dsi-phy-28nm-8960", |
269 | .data = &dsi_phy_28nm_8960_cfgs }, | 392 | .data = &dsi_phy_28nm_8960_cfgs }, |
270 | #endif | 393 | #endif |
394 | #ifdef CONFIG_DRM_MSM_DSI_14NM_PHY | ||
395 | { .compatible = "qcom,dsi-phy-14nm", | ||
396 | .data = &dsi_phy_14nm_cfgs }, | ||
397 | #endif | ||
271 | {} | 398 | {} |
272 | }; | 399 | }; |
273 | 400 | ||
@@ -295,6 +422,24 @@ static int dsi_phy_get_id(struct msm_dsi_phy *phy) | |||
295 | return -EINVAL; | 422 | return -EINVAL; |
296 | } | 423 | } |
297 | 424 | ||
425 | int msm_dsi_phy_init_common(struct msm_dsi_phy *phy) | ||
426 | { | ||
427 | struct platform_device *pdev = phy->pdev; | ||
428 | int ret = 0; | ||
429 | |||
430 | phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", | ||
431 | "DSI_PHY_REG"); | ||
432 | if (IS_ERR(phy->reg_base)) { | ||
433 | dev_err(&pdev->dev, "%s: failed to map phy regulator base\n", | ||
434 | __func__); | ||
435 | ret = -ENOMEM; | ||
436 | goto fail; | ||
437 | } | ||
438 | |||
439 | fail: | ||
440 | return ret; | ||
441 | } | ||
442 | |||
298 | static int dsi_phy_driver_probe(struct platform_device *pdev) | 443 | static int dsi_phy_driver_probe(struct platform_device *pdev) |
299 | { | 444 | { |
300 | struct msm_dsi_phy *phy; | 445 | struct msm_dsi_phy *phy; |
@@ -331,15 +476,6 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) | |||
331 | goto fail; | 476 | goto fail; |
332 | } | 477 | } |
333 | 478 | ||
334 | phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", | ||
335 | "DSI_PHY_REG"); | ||
336 | if (IS_ERR(phy->reg_base)) { | ||
337 | dev_err(dev, "%s: failed to map phy regulator base\n", | ||
338 | __func__); | ||
339 | ret = -ENOMEM; | ||
340 | goto fail; | ||
341 | } | ||
342 | |||
343 | ret = dsi_phy_regulator_init(phy); | 479 | ret = dsi_phy_regulator_init(phy); |
344 | if (ret) { | 480 | if (ret) { |
345 | dev_err(dev, "%s: failed to init regulator\n", __func__); | 481 | dev_err(dev, "%s: failed to init regulator\n", __func__); |
@@ -353,6 +489,12 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) | |||
353 | goto fail; | 489 | goto fail; |
354 | } | 490 | } |
355 | 491 | ||
492 | if (phy->cfg->ops.init) { | ||
493 | ret = phy->cfg->ops.init(phy); | ||
494 | if (ret) | ||
495 | goto fail; | ||
496 | } | ||
497 | |||
356 | /* PLL init will call into clk_register which requires | 498 | /* PLL init will call into clk_register which requires |
357 | * register access, so we need to enable power and ahb clock. | 499 | * register access, so we need to enable power and ahb clock. |
358 | */ | 500 | */ |
@@ -410,7 +552,7 @@ void __exit msm_dsi_phy_driver_unregister(void) | |||
410 | } | 552 | } |
411 | 553 | ||
412 | int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, | 554 | int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, |
413 | const unsigned long bit_rate, const unsigned long esc_rate) | 555 | struct msm_dsi_phy_clk_request *clk_req) |
414 | { | 556 | { |
415 | struct device *dev = &phy->pdev->dev; | 557 | struct device *dev = &phy->pdev->dev; |
416 | int ret; | 558 | int ret; |
@@ -418,21 +560,52 @@ int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, | |||
418 | if (!phy || !phy->cfg->ops.enable) | 560 | if (!phy || !phy->cfg->ops.enable) |
419 | return -EINVAL; | 561 | return -EINVAL; |
420 | 562 | ||
563 | ret = dsi_phy_enable_resource(phy); | ||
564 | if (ret) { | ||
565 | dev_err(dev, "%s: resource enable failed, %d\n", | ||
566 | __func__, ret); | ||
567 | goto res_en_fail; | ||
568 | } | ||
569 | |||
421 | ret = dsi_phy_regulator_enable(phy); | 570 | ret = dsi_phy_regulator_enable(phy); |
422 | if (ret) { | 571 | if (ret) { |
423 | dev_err(dev, "%s: regulator enable failed, %d\n", | 572 | dev_err(dev, "%s: regulator enable failed, %d\n", |
424 | __func__, ret); | 573 | __func__, ret); |
425 | return ret; | 574 | goto reg_en_fail; |
426 | } | 575 | } |
427 | 576 | ||
428 | ret = phy->cfg->ops.enable(phy, src_pll_id, bit_rate, esc_rate); | 577 | ret = phy->cfg->ops.enable(phy, src_pll_id, clk_req); |
429 | if (ret) { | 578 | if (ret) { |
430 | dev_err(dev, "%s: phy enable failed, %d\n", __func__, ret); | 579 | dev_err(dev, "%s: phy enable failed, %d\n", __func__, ret); |
431 | dsi_phy_regulator_disable(phy); | 580 | goto phy_en_fail; |
432 | return ret; | 581 | } |
582 | |||
583 | /* | ||
584 | * Resetting DSI PHY silently changes its PLL registers to reset status, | ||
585 | * which will confuse clock driver and result in wrong output rate of | ||
586 | * link clocks. Restore PLL status if its PLL is being used as clock | ||
587 | * source. | ||
588 | */ | ||
589 | if (phy->usecase != MSM_DSI_PHY_SLAVE) { | ||
590 | ret = msm_dsi_pll_restore_state(phy->pll); | ||
591 | if (ret) { | ||
592 | dev_err(dev, "%s: failed to restore pll state, %d\n", | ||
593 | __func__, ret); | ||
594 | goto pll_restor_fail; | ||
595 | } | ||
433 | } | 596 | } |
434 | 597 | ||
435 | return 0; | 598 | return 0; |
599 | |||
600 | pll_restor_fail: | ||
601 | if (phy->cfg->ops.disable) | ||
602 | phy->cfg->ops.disable(phy); | ||
603 | phy_en_fail: | ||
604 | dsi_phy_regulator_disable(phy); | ||
605 | reg_en_fail: | ||
606 | dsi_phy_disable_resource(phy); | ||
607 | res_en_fail: | ||
608 | return ret; | ||
436 | } | 609 | } |
437 | 610 | ||
438 | void msm_dsi_phy_disable(struct msm_dsi_phy *phy) | 611 | void msm_dsi_phy_disable(struct msm_dsi_phy *phy) |
@@ -440,21 +613,21 @@ void msm_dsi_phy_disable(struct msm_dsi_phy *phy) | |||
440 | if (!phy || !phy->cfg->ops.disable) | 613 | if (!phy || !phy->cfg->ops.disable) |
441 | return; | 614 | return; |
442 | 615 | ||
616 | /* Save PLL status if it is a clock source */ | ||
617 | if (phy->usecase != MSM_DSI_PHY_SLAVE) | ||
618 | msm_dsi_pll_save_state(phy->pll); | ||
619 | |||
443 | phy->cfg->ops.disable(phy); | 620 | phy->cfg->ops.disable(phy); |
444 | 621 | ||
445 | dsi_phy_regulator_disable(phy); | 622 | dsi_phy_regulator_disable(phy); |
623 | dsi_phy_disable_resource(phy); | ||
446 | } | 624 | } |
447 | 625 | ||
448 | void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, | 626 | void msm_dsi_phy_get_shared_timings(struct msm_dsi_phy *phy, |
449 | u32 *clk_pre, u32 *clk_post) | 627 | struct msm_dsi_phy_shared_timings *shared_timings) |
450 | { | 628 | { |
451 | if (!phy) | 629 | memcpy(shared_timings, &phy->timing.shared_timings, |
452 | return; | 630 | sizeof(*shared_timings)); |
453 | |||
454 | if (clk_pre) | ||
455 | *clk_pre = phy->timing.clk_pre; | ||
456 | if (clk_post) | ||
457 | *clk_post = phy->timing.clk_post; | ||
458 | } | 631 | } |
459 | 632 | ||
460 | struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) | 633 | struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) |
@@ -465,3 +638,9 @@ struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) | |||
465 | return phy->pll; | 638 | return phy->pll; |
466 | } | 639 | } |
467 | 640 | ||
641 | void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy, | ||
642 | enum msm_dsi_phy_usecase uc) | ||
643 | { | ||
644 | if (phy) | ||
645 | phy->usecase = uc; | ||
646 | } | ||