brintos

brintos / linux-shallow public Read only

0
0
Text · 25.2 KiB · dd58bc0 Raw
882 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2015, The Linux Foundation. All rights reserved.4 */5 6#include <linux/clk-provider.h>7#include <linux/platform_device.h>8#include <dt-bindings/phy/phy.h>9 10#include "dsi_phy.h"11 12#define S_DIV_ROUND_UP(n, d)	\13	(((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d)))14 15static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent,16				s32 min_result, bool even)17{18	s32 v;19 20	v = (tmax - tmin) * percent;21	v = S_DIV_ROUND_UP(v, 100) + tmin;22	if (even && (v & 0x1))23		return max_t(s32, min_result, v - 1);24	else25		return max_t(s32, min_result, v);26}27 28static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing,29					s32 ui, s32 coeff, s32 pcnt)30{31	s32 tmax, tmin, clk_z;32	s32 temp;33 34	/* reset */35	temp = 300 * coeff - ((timing->clk_prepare >> 1) + 1) * 2 * ui;36	tmin = S_DIV_ROUND_UP(temp, ui) - 2;37	if (tmin > 255) {38		tmax = 511;39		clk_z = linear_inter(2 * tmin, tmin, pcnt, 0, true);40	} else {41		tmax = 255;42		clk_z = linear_inter(tmax, tmin, pcnt, 0, true);43	}44 45	/* adjust */46	temp = (timing->hs_rqst + timing->clk_prepare + clk_z) & 0x7;47	timing->clk_zero = clk_z + 8 - temp;48}49 50int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing,51			     struct msm_dsi_phy_clk_request *clk_req)52{53	const unsigned long bit_rate = clk_req->bitclk_rate;54	const unsigned long esc_rate = clk_req->escclk_rate;55	s32 ui, lpx;56	s32 tmax, tmin;57	s32 pcnt0 = 10;58	s32 pcnt1 = (bit_rate > 1200000000) ? 15 : 10;59	s32 pcnt2 = 10;60	s32 pcnt3 = (bit_rate > 180000000) ? 10 : 40;61	s32 coeff = 1000; /* Precision, should avoid overflow */62	s32 temp;63 64	if (!bit_rate || !esc_rate)65		return -EINVAL;66 67	ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);68	lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000);69 70	tmax = S_DIV_ROUND_UP(95 * coeff, ui) - 2;71	tmin = S_DIV_ROUND_UP(38 * coeff, ui) - 2;72	timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, true);73 74	temp = lpx / ui;75	if (temp & 0x1)76		timing->hs_rqst = temp;77	else78		timing->hs_rqst = max_t(s32, 0, temp - 2);79 80	/* Calculate clk_zero after clk_prepare and hs_rqst */81	dsi_dphy_timing_calc_clk_zero(timing, ui, coeff, pcnt2);82 83	temp = 105 * coeff + 12 * ui - 20 * coeff;84	tmax = S_DIV_ROUND_UP(temp, ui) - 2;85	tmin = S_DIV_ROUND_UP(60 * coeff, ui) - 2;86	timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, true);87 88	temp = 85 * coeff + 6 * ui;89	tmax = S_DIV_ROUND_UP(temp, ui) - 2;90	temp = 40 * coeff + 4 * ui;91	tmin = S_DIV_ROUND_UP(temp, ui) - 2;92	timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, true);93 94	tmax = 255;95	temp = ((timing->hs_prepare >> 1) + 1) * 2 * ui + 2 * ui;96	temp = 145 * coeff + 10 * ui - temp;97	tmin = S_DIV_ROUND_UP(temp, ui) - 2;98	timing->hs_zero = linear_inter(tmax, tmin, pcnt2, 24, true);99 100	temp = 105 * coeff + 12 * ui - 20 * coeff;101	tmax = S_DIV_ROUND_UP(temp, ui) - 2;102	temp = 60 * coeff + 4 * ui;103	tmin = DIV_ROUND_UP(temp, ui) - 2;104	timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, true);105 106	tmax = 255;107	tmin = S_DIV_ROUND_UP(100 * coeff, ui) - 2;108	timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, true);109 110	tmax = 63;111	temp = ((timing->hs_exit >> 1) + 1) * 2 * ui;112	temp = 60 * coeff + 52 * ui - 24 * ui - temp;113	tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1;114	timing->shared_timings.clk_post = linear_inter(tmax, tmin, pcnt2, 0,115						       false);116	tmax = 63;117	temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui;118	temp += ((timing->clk_zero >> 1) + 1) * 2 * ui;119	temp += 8 * ui + lpx;120	tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1;121	if (tmin > tmax) {122		temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false);123		timing->shared_timings.clk_pre = temp >> 1;124		timing->shared_timings.clk_pre_inc_by_2 = true;125	} else {126		timing->shared_timings.clk_pre =127				linear_inter(tmax, tmin, pcnt2, 0, false);128		timing->shared_timings.clk_pre_inc_by_2 = false;129	}130 131	timing->ta_go = 3;132	timing->ta_sure = 0;133	timing->ta_get = 4;134 135	DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",136		timing->shared_timings.clk_pre, timing->shared_timings.clk_post,137		timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,138		timing->clk_trail, timing->clk_prepare, timing->hs_exit,139		timing->hs_zero, timing->hs_prepare, timing->hs_trail,140		timing->hs_rqst);141 142	return 0;143}144 145int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,146				struct msm_dsi_phy_clk_request *clk_req)147{148	const unsigned long bit_rate = clk_req->bitclk_rate;149	const unsigned long esc_rate = clk_req->escclk_rate;150	s32 ui, ui_x8;151	s32 tmax, tmin;152	s32 pcnt0 = 50;153	s32 pcnt1 = 50;154	s32 pcnt2 = 10;155	s32 pcnt3 = 30;156	s32 pcnt4 = 10;157	s32 pcnt5 = 2;158	s32 coeff = 1000; /* Precision, should avoid overflow */159	s32 hb_en, hb_en_ckln, pd_ckln, pd;160	s32 val, val_ckln;161	s32 temp;162 163	if (!bit_rate || !esc_rate)164		return -EINVAL;165 166	timing->hs_halfbyte_en = 0;167	hb_en = 0;168	timing->hs_halfbyte_en_ckln = 0;169	hb_en_ckln = 0;170	timing->hs_prep_dly_ckln = (bit_rate > 100000000) ? 0 : 3;171	pd_ckln = timing->hs_prep_dly_ckln;172	timing->hs_prep_dly = (bit_rate > 120000000) ? 0 : 1;173	pd = timing->hs_prep_dly;174 175	val = (hb_en << 2) + (pd << 1);176	val_ckln = (hb_en_ckln << 2) + (pd_ckln << 1);177 178	ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);179	ui_x8 = ui << 3;180 181	temp = S_DIV_ROUND_UP(38 * coeff - val_ckln * ui, ui_x8);182	tmin = max_t(s32, temp, 0);183	temp = (95 * coeff - val_ckln * ui) / ui_x8;184	tmax = max_t(s32, temp, 0);185	timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false);186 187	temp = 300 * coeff - ((timing->clk_prepare << 3) + val_ckln) * ui;188	tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3;189	tmax = (tmin > 255) ? 511 : 255;190	timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false);191 192	tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);193	temp = 105 * coeff + 12 * ui - 20 * coeff;194	tmax = (temp + 3 * ui) / ui_x8;195	timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false);196 197	temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui - val * ui, ui_x8);198	tmin = max_t(s32, temp, 0);199	temp = (85 * coeff + 6 * ui - val * ui) / ui_x8;200	tmax = max_t(s32, temp, 0);201	timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false);202 203	temp = 145 * coeff + 10 * ui - ((timing->hs_prepare << 3) + val) * ui;204	tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3;205	tmax = 255;206	timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false);207 208	tmin = DIV_ROUND_UP(60 * coeff + 4 * ui + 3 * ui, ui_x8);209	temp = 105 * coeff + 12 * ui - 20 * coeff;210	tmax = (temp + 3 * ui) / ui_x8;211	timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false);212 213	temp = 50 * coeff + ((hb_en << 2) - 8) * ui;214	timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);215 216	tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;217	tmax = 255;218	timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false);219 220	temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui;221	timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8);222 223	temp = 60 * coeff + 52 * ui - 43 * ui;224	tmin = DIV_ROUND_UP(temp, ui_x8) - 1;225	tmax = 63;226	timing->shared_timings.clk_post =227				linear_inter(tmax, tmin, pcnt2, 0, false);228 229	temp = 8 * ui + ((timing->clk_prepare << 3) + val_ckln) * ui;230	temp += (((timing->clk_zero + 3) << 3) + 11 - (pd_ckln << 1)) * ui;231	temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) :232				(((timing->hs_rqst_ckln << 3) + 8) * ui);233	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;234	tmax = 63;235	if (tmin > tmax) {236		temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false);237		timing->shared_timings.clk_pre = temp >> 1;238		timing->shared_timings.clk_pre_inc_by_2 = 1;239	} else {240		timing->shared_timings.clk_pre =241				linear_inter(tmax, tmin, pcnt2, 0, false);242		timing->shared_timings.clk_pre_inc_by_2 = 0;243	}244 245	timing->ta_go = 3;246	timing->ta_sure = 0;247	timing->ta_get = 4;248 249	DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",250	    timing->shared_timings.clk_pre, timing->shared_timings.clk_post,251	    timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,252	    timing->clk_trail, timing->clk_prepare, timing->hs_exit,253	    timing->hs_zero, timing->hs_prepare, timing->hs_trail,254	    timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en,255	    timing->hs_halfbyte_en_ckln, timing->hs_prep_dly,256	    timing->hs_prep_dly_ckln);257 258	return 0;259}260 261int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,262	struct msm_dsi_phy_clk_request *clk_req)263{264	const unsigned long bit_rate = clk_req->bitclk_rate;265	const unsigned long esc_rate = clk_req->escclk_rate;266	s32 ui, ui_x8;267	s32 tmax, tmin;268	s32 pcnt0 = 50;269	s32 pcnt1 = 50;270	s32 pcnt2 = 10;271	s32 pcnt3 = 30;272	s32 pcnt4 = 10;273	s32 pcnt5 = 2;274	s32 coeff = 1000; /* Precision, should avoid overflow */275	s32 hb_en, hb_en_ckln;276	s32 temp;277 278	if (!bit_rate || !esc_rate)279		return -EINVAL;280 281	timing->hs_halfbyte_en = 0;282	hb_en = 0;283	timing->hs_halfbyte_en_ckln = 0;284	hb_en_ckln = 0;285 286	ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);287	ui_x8 = ui << 3;288 289	temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);290	tmin = max_t(s32, temp, 0);291	temp = (95 * coeff) / ui_x8;292	tmax = max_t(s32, temp, 0);293	timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false);294 295	temp = 300 * coeff - (timing->clk_prepare << 3) * ui;296	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;297	tmax = (tmin > 255) ? 511 : 255;298	timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false);299 300	tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);301	temp = 105 * coeff + 12 * ui - 20 * coeff;302	tmax = (temp + 3 * ui) / ui_x8;303	timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false);304 305	temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);306	tmin = max_t(s32, temp, 0);307	temp = (85 * coeff + 6 * ui) / ui_x8;308	tmax = max_t(s32, temp, 0);309	timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false);310 311	temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;312	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;313	tmax = 255;314	timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false);315 316	tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;317	temp = 105 * coeff + 12 * ui - 20 * coeff;318	tmax = (temp / ui_x8) - 1;319	timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false);320 321	temp = 50 * coeff + ((hb_en << 2) - 8) * ui;322	timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);323 324	tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;325	tmax = 255;326	timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false);327 328	temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui;329	timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8);330 331	temp = 60 * coeff + 52 * ui - 43 * ui;332	tmin = DIV_ROUND_UP(temp, ui_x8) - 1;333	tmax = 63;334	timing->shared_timings.clk_post =335		linear_inter(tmax, tmin, pcnt2, 0, false);336 337	temp = 8 * ui + (timing->clk_prepare << 3) * ui;338	temp += (((timing->clk_zero + 3) << 3) + 11) * ui;339	temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) :340		(((timing->hs_rqst_ckln << 3) + 8) * ui);341	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;342	tmax = 63;343	if (tmin > tmax) {344		temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false);345		timing->shared_timings.clk_pre = temp >> 1;346		timing->shared_timings.clk_pre_inc_by_2 = 1;347	} else {348		timing->shared_timings.clk_pre =349			linear_inter(tmax, tmin, pcnt2, 0, false);350		timing->shared_timings.clk_pre_inc_by_2 = 0;351	}352 353	timing->shared_timings.byte_intf_clk_div_2 = true;354 355	timing->ta_go = 3;356	timing->ta_sure = 0;357	timing->ta_get = 4;358 359	DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",360		timing->shared_timings.clk_pre, timing->shared_timings.clk_post,361		timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,362		timing->clk_trail, timing->clk_prepare, timing->hs_exit,363		timing->hs_zero, timing->hs_prepare, timing->hs_trail,364		timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en,365		timing->hs_halfbyte_en_ckln, timing->hs_prep_dly,366		timing->hs_prep_dly_ckln);367 368	return 0;369}370 371int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,372	struct msm_dsi_phy_clk_request *clk_req)373{374	const unsigned long bit_rate = clk_req->bitclk_rate;375	const unsigned long esc_rate = clk_req->escclk_rate;376	s32 ui, ui_x8;377	s32 tmax, tmin;378	s32 pcnt_clk_prep = 50;379	s32 pcnt_clk_zero = 2;380	s32 pcnt_clk_trail = 30;381	s32 pcnt_hs_prep = 50;382	s32 pcnt_hs_zero = 10;383	s32 pcnt_hs_trail = 30;384	s32 pcnt_hs_exit = 10;385	s32 coeff = 1000; /* Precision, should avoid overflow */386	s32 hb_en;387	s32 temp;388 389	if (!bit_rate || !esc_rate)390		return -EINVAL;391 392	hb_en = 0;393 394	ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);395	ui_x8 = ui << 3;396 397	/* TODO: verify these calculations against latest downstream driver398	 * everything except clk_post/clk_pre uses calculations from v3 based399	 * on the downstream driver having the same calculations for v3 and v4400	 */401 402	temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);403	tmin = max_t(s32, temp, 0);404	temp = (95 * coeff) / ui_x8;405	tmax = max_t(s32, temp, 0);406	timing->clk_prepare = linear_inter(tmax, tmin, pcnt_clk_prep, 0, false);407 408	temp = 300 * coeff - (timing->clk_prepare << 3) * ui;409	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;410	tmax = (tmin > 255) ? 511 : 255;411	timing->clk_zero = linear_inter(tmax, tmin, pcnt_clk_zero, 0, false);412 413	tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);414	temp = 105 * coeff + 12 * ui - 20 * coeff;415	tmax = (temp + 3 * ui) / ui_x8;416	timing->clk_trail = linear_inter(tmax, tmin, pcnt_clk_trail, 0, false);417 418	temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);419	tmin = max_t(s32, temp, 0);420	temp = (85 * coeff + 6 * ui) / ui_x8;421	tmax = max_t(s32, temp, 0);422	timing->hs_prepare = linear_inter(tmax, tmin, pcnt_hs_prep, 0, false);423 424	temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;425	tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;426	tmax = 255;427	timing->hs_zero = linear_inter(tmax, tmin, pcnt_hs_zero, 0, false);428 429	tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;430	temp = 105 * coeff + 12 * ui - 20 * coeff;431	tmax = (temp / ui_x8) - 1;432	timing->hs_trail = linear_inter(tmax, tmin, pcnt_hs_trail, 0, false);433 434	temp = 50 * coeff + ((hb_en << 2) - 8) * ui;435	timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);436 437	tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;438	tmax = 255;439	timing->hs_exit = linear_inter(tmax, tmin, pcnt_hs_exit, 0, false);440 441	/* recommended min442	 * = roundup((mipi_min_ns + t_hs_trail_ns)/(16*bit_clk_ns), 0) - 1443	 */444	temp = 60 * coeff + 52 * ui + + (timing->hs_trail + 1) * ui_x8;445	tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;446	tmax = 255;447	timing->shared_timings.clk_post = linear_inter(tmax, tmin, 5, 0, false);448 449	/* recommended min450	 * val1 = (tlpx_ns + clk_prepare_ns + clk_zero_ns + hs_rqst_ns)451	 * val2 = (16 * bit_clk_ns)452	 * final = roundup(val1/val2, 0) - 1453	 */454	temp = 52 * coeff + (timing->clk_prepare + timing->clk_zero + 1) * ui_x8 + 54 * coeff;455	tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;456	tmax = 255;457	timing->shared_timings.clk_pre = DIV_ROUND_UP((tmax - tmin) * 125, 10000) + tmin;458 459	timing->shared_timings.byte_intf_clk_div_2 = true;460 461	DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d",462		timing->shared_timings.clk_pre, timing->shared_timings.clk_post,463		timing->clk_zero, timing->clk_trail, timing->clk_prepare, timing->hs_exit,464		timing->hs_zero, timing->hs_prepare, timing->hs_trail, timing->hs_rqst);465 466	return 0;467}468 469int msm_dsi_cphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,470	struct msm_dsi_phy_clk_request *clk_req)471{472	const unsigned long bit_rate = clk_req->bitclk_rate;473	const unsigned long esc_rate = clk_req->escclk_rate;474	s32 ui, ui_x7;475	s32 tmax, tmin;476	s32 coeff = 1000; /* Precision, should avoid overflow */477	s32 temp;478 479	if (!bit_rate || !esc_rate)480		return -EINVAL;481 482	ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);483	ui_x7 = ui * 7;484 485	temp = S_DIV_ROUND_UP(38 * coeff, ui_x7);486	tmin = max_t(s32, temp, 0);487	temp = (95 * coeff) / ui_x7;488	tmax = max_t(s32, temp, 0);489	timing->clk_prepare = linear_inter(tmax, tmin, 50, 0, false);490 491	tmin = DIV_ROUND_UP(50 * coeff, ui_x7);492	tmax = 255;493	timing->hs_rqst = linear_inter(tmax, tmin, 1, 0, false);494 495	tmin = DIV_ROUND_UP(100 * coeff, ui_x7) - 1;496	tmax = 255;497	timing->hs_exit = linear_inter(tmax, tmin, 10, 0, false);498 499	tmin = 1;500	tmax = 32;501	timing->shared_timings.clk_post = linear_inter(tmax, tmin, 80, 0, false);502 503	tmin = min_t(s32, 64, S_DIV_ROUND_UP(262 * coeff, ui_x7) - 1);504	tmax = 64;505	timing->shared_timings.clk_pre = linear_inter(tmax, tmin, 20, 0, false);506 507	DBG("%d, %d, %d, %d, %d",508		timing->shared_timings.clk_pre, timing->shared_timings.clk_post,509		timing->clk_prepare, timing->hs_exit, timing->hs_rqst);510 511	return 0;512}513 514static int dsi_phy_enable_resource(struct msm_dsi_phy *phy)515{516	struct device *dev = &phy->pdev->dev;517	int ret;518 519	ret = pm_runtime_resume_and_get(dev);520	if (ret)521		return ret;522 523	ret = clk_prepare_enable(phy->ahb_clk);524	if (ret) {525		DRM_DEV_ERROR(dev, "%s: can't enable ahb clk, %d\n", __func__, ret);526		pm_runtime_put_sync(dev);527	}528 529	return ret;530}531 532static void dsi_phy_disable_resource(struct msm_dsi_phy *phy)533{534	clk_disable_unprepare(phy->ahb_clk);535	pm_runtime_put(&phy->pdev->dev);536}537 538static const struct of_device_id dsi_phy_dt_match[] = {539#ifdef CONFIG_DRM_MSM_DSI_28NM_PHY540	{ .compatible = "qcom,dsi-phy-28nm-hpm",541	  .data = &dsi_phy_28nm_hpm_cfgs },542	{ .compatible = "qcom,dsi-phy-28nm-hpm-fam-b",543	  .data = &dsi_phy_28nm_hpm_famb_cfgs },544	{ .compatible = "qcom,dsi-phy-28nm-lp",545	  .data = &dsi_phy_28nm_lp_cfgs },546	{ .compatible = "qcom,dsi-phy-28nm-8226",547	  .data = &dsi_phy_28nm_8226_cfgs },548	{ .compatible = "qcom,dsi-phy-28nm-8937",549	  .data = &dsi_phy_28nm_8937_cfgs },550#endif551#ifdef CONFIG_DRM_MSM_DSI_20NM_PHY552	{ .compatible = "qcom,dsi-phy-20nm",553	  .data = &dsi_phy_20nm_cfgs },554#endif555#ifdef CONFIG_DRM_MSM_DSI_28NM_8960_PHY556	{ .compatible = "qcom,dsi-phy-28nm-8960",557	  .data = &dsi_phy_28nm_8960_cfgs },558#endif559#ifdef CONFIG_DRM_MSM_DSI_14NM_PHY560	{ .compatible = "qcom,dsi-phy-14nm",561	  .data = &dsi_phy_14nm_cfgs },562	{ .compatible = "qcom,dsi-phy-14nm-2290",563	  .data = &dsi_phy_14nm_2290_cfgs },564	{ .compatible = "qcom,dsi-phy-14nm-660",565	  .data = &dsi_phy_14nm_660_cfgs },566	{ .compatible = "qcom,dsi-phy-14nm-8953",567	  .data = &dsi_phy_14nm_8953_cfgs },568	{ .compatible = "qcom,sm6125-dsi-phy-14nm",569	  .data = &dsi_phy_14nm_2290_cfgs },570#endif571#ifdef CONFIG_DRM_MSM_DSI_10NM_PHY572	{ .compatible = "qcom,dsi-phy-10nm",573	  .data = &dsi_phy_10nm_cfgs },574	{ .compatible = "qcom,dsi-phy-10nm-8998",575	  .data = &dsi_phy_10nm_8998_cfgs },576#endif577#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY578	{ .compatible = "qcom,dsi-phy-7nm",579	  .data = &dsi_phy_7nm_cfgs },580	{ .compatible = "qcom,dsi-phy-7nm-8150",581	  .data = &dsi_phy_7nm_8150_cfgs },582	{ .compatible = "qcom,sc7280-dsi-phy-7nm",583	  .data = &dsi_phy_7nm_7280_cfgs },584	{ .compatible = "qcom,sm6375-dsi-phy-7nm",585	  .data = &dsi_phy_7nm_6375_cfgs },586	{ .compatible = "qcom,sm8350-dsi-phy-5nm",587	  .data = &dsi_phy_5nm_8350_cfgs },588	{ .compatible = "qcom,sm8450-dsi-phy-5nm",589	  .data = &dsi_phy_5nm_8450_cfgs },590	{ .compatible = "qcom,sm8550-dsi-phy-4nm",591	  .data = &dsi_phy_4nm_8550_cfgs },592	{ .compatible = "qcom,sm8650-dsi-phy-4nm",593	  .data = &dsi_phy_4nm_8650_cfgs },594#endif595	{}596};597 598/*599 * Currently, we only support one SoC for each PHY type. When we have multiple600 * SoCs for the same PHY, we can try to make the index searching a bit more601 * clever.602 */603static int dsi_phy_get_id(struct msm_dsi_phy *phy)604{605	struct platform_device *pdev = phy->pdev;606	const struct msm_dsi_phy_cfg *cfg = phy->cfg;607	struct resource *res;608	int i;609 610	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_phy");611	if (!res)612		return -EINVAL;613 614	for (i = 0; i < cfg->num_dsi_phy; i++) {615		if (cfg->io_start[i] == res->start)616			return i;617	}618 619	return -EINVAL;620}621 622static int dsi_phy_driver_probe(struct platform_device *pdev)623{624	struct msm_dsi_phy *phy;625	struct device *dev = &pdev->dev;626	u32 phy_type;627	int ret;628 629	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);630	if (!phy)631		return -ENOMEM;632 633	phy->provided_clocks = devm_kzalloc(dev,634			struct_size(phy->provided_clocks, hws, NUM_PROVIDED_CLKS),635			GFP_KERNEL);636	if (!phy->provided_clocks)637		return -ENOMEM;638 639	phy->provided_clocks->num = NUM_PROVIDED_CLKS;640 641	phy->cfg = of_device_get_match_data(&pdev->dev);642	if (!phy->cfg)643		return -ENODEV;644 645	phy->pdev = pdev;646 647	phy->id = dsi_phy_get_id(phy);648	if (phy->id < 0)649		return dev_err_probe(dev, phy->id,650				     "Couldn't identify PHY index\n");651 652	phy->regulator_ldo_mode = of_property_read_bool(dev->of_node,653				"qcom,dsi-phy-regulator-ldo-mode");654	if (!of_property_read_u32(dev->of_node, "phy-type", &phy_type))655		phy->cphy_mode = (phy_type == PHY_TYPE_CPHY);656 657	phy->base = msm_ioremap_size(pdev, "dsi_phy", &phy->base_size);658	if (IS_ERR(phy->base))659		return dev_err_probe(dev, PTR_ERR(phy->base),660				     "Failed to map phy base\n");661 662	phy->pll_base = msm_ioremap_size(pdev, "dsi_pll", &phy->pll_size);663	if (IS_ERR(phy->pll_base))664		return dev_err_probe(dev, PTR_ERR(phy->pll_base),665				     "Failed to map pll base\n");666 667	if (phy->cfg->has_phy_lane) {668		phy->lane_base = msm_ioremap_size(pdev, "dsi_phy_lane", &phy->lane_size);669		if (IS_ERR(phy->lane_base))670			return dev_err_probe(dev, PTR_ERR(phy->lane_base),671					     "Failed to map phy lane base\n");672	}673 674	if (phy->cfg->has_phy_regulator) {675		phy->reg_base = msm_ioremap_size(pdev, "dsi_phy_regulator", &phy->reg_size);676		if (IS_ERR(phy->reg_base))677			return dev_err_probe(dev, PTR_ERR(phy->reg_base),678					     "Failed to map phy regulator base\n");679	}680 681	if (phy->cfg->ops.parse_dt_properties) {682		ret = phy->cfg->ops.parse_dt_properties(phy);683		if (ret)684			return ret;685	}686 687	ret = devm_regulator_bulk_get_const(dev, phy->cfg->num_regulators,688					    phy->cfg->regulator_data,689					    &phy->supplies);690	if (ret)691		return ret;692 693	phy->ahb_clk = msm_clk_get(pdev, "iface");694	if (IS_ERR(phy->ahb_clk))695		return dev_err_probe(dev, PTR_ERR(phy->ahb_clk),696				     "Unable to get ahb clk\n");697 698	ret = devm_pm_runtime_enable(&pdev->dev);699	if (ret)700		return ret;701 702	/* PLL init will call into clk_register which requires703	 * register access, so we need to enable power and ahb clock.704	 */705	ret = dsi_phy_enable_resource(phy);706	if (ret)707		return ret;708 709	if (phy->cfg->ops.pll_init) {710		ret = phy->cfg->ops.pll_init(phy);711		if (ret)712			return dev_err_probe(dev, ret,713					     "PLL init failed; need separate clk driver\n");714	}715 716	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,717				     phy->provided_clocks);718	if (ret)719		return dev_err_probe(dev, ret,720				     "Failed to register clk provider\n");721 722	dsi_phy_disable_resource(phy);723 724	platform_set_drvdata(pdev, phy);725 726	return 0;727}728 729static struct platform_driver dsi_phy_platform_driver = {730	.probe      = dsi_phy_driver_probe,731	.driver     = {732		.name   = "msm_dsi_phy",733		.of_match_table = dsi_phy_dt_match,734	},735};736 737void __init msm_dsi_phy_driver_register(void)738{739	platform_driver_register(&dsi_phy_platform_driver);740}741 742void __exit msm_dsi_phy_driver_unregister(void)743{744	platform_driver_unregister(&dsi_phy_platform_driver);745}746 747int msm_dsi_phy_enable(struct msm_dsi_phy *phy,748			struct msm_dsi_phy_clk_request *clk_req,749			struct msm_dsi_phy_shared_timings *shared_timings)750{751	struct device *dev;752	int ret;753 754	if (!phy || !phy->cfg->ops.enable)755		return -EINVAL;756 757	dev = &phy->pdev->dev;758 759	ret = dsi_phy_enable_resource(phy);760	if (ret) {761		DRM_DEV_ERROR(dev, "%s: resource enable failed, %d\n",762			__func__, ret);763		goto res_en_fail;764	}765 766	ret = regulator_bulk_enable(phy->cfg->num_regulators, phy->supplies);767	if (ret) {768		DRM_DEV_ERROR(dev, "%s: regulator enable failed, %d\n",769			__func__, ret);770		goto reg_en_fail;771	}772 773	ret = phy->cfg->ops.enable(phy, clk_req);774	if (ret) {775		DRM_DEV_ERROR(dev, "%s: phy enable failed, %d\n", __func__, ret);776		goto phy_en_fail;777	}778 779	memcpy(shared_timings, &phy->timing.shared_timings,780	       sizeof(*shared_timings));781 782	/*783	 * Resetting DSI PHY silently changes its PLL registers to reset status,784	 * which will confuse clock driver and result in wrong output rate of785	 * link clocks. Restore PLL status if its PLL is being used as clock786	 * source.787	 */788	if (phy->usecase != MSM_DSI_PHY_SLAVE) {789		ret = msm_dsi_phy_pll_restore_state(phy);790		if (ret) {791			DRM_DEV_ERROR(dev, "%s: failed to restore phy state, %d\n",792				__func__, ret);793			goto pll_restor_fail;794		}795	}796 797	return 0;798 799pll_restor_fail:800	if (phy->cfg->ops.disable)801		phy->cfg->ops.disable(phy);802phy_en_fail:803	regulator_bulk_disable(phy->cfg->num_regulators, phy->supplies);804reg_en_fail:805	dsi_phy_disable_resource(phy);806res_en_fail:807	return ret;808}809 810void msm_dsi_phy_disable(struct msm_dsi_phy *phy)811{812	if (!phy || !phy->cfg->ops.disable)813		return;814 815	phy->cfg->ops.disable(phy);816 817	regulator_bulk_disable(phy->cfg->num_regulators, phy->supplies);818	dsi_phy_disable_resource(phy);819}820 821void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy,822			     enum msm_dsi_phy_usecase uc)823{824	if (phy)825		phy->usecase = uc;826}827 828/* Returns true if we have to clear DSI_LANE_CTRL.HS_REQ_SEL_PHY */829bool msm_dsi_phy_set_continuous_clock(struct msm_dsi_phy *phy, bool enable)830{831	if (!phy || !phy->cfg->ops.set_continuous_clock)832		return false;833 834	return phy->cfg->ops.set_continuous_clock(phy, enable);835}836 837void msm_dsi_phy_pll_save_state(struct msm_dsi_phy *phy)838{839	if (phy->cfg->ops.save_pll_state) {840		phy->cfg->ops.save_pll_state(phy);841		phy->state_saved = true;842	}843}844 845int msm_dsi_phy_pll_restore_state(struct msm_dsi_phy *phy)846{847	int ret;848 849	if (phy->cfg->ops.restore_pll_state && phy->state_saved) {850		ret = phy->cfg->ops.restore_pll_state(phy);851		if (ret)852			return ret;853 854		phy->state_saved = false;855	}856 857	return 0;858}859 860void msm_dsi_phy_snapshot(struct msm_disp_state *disp_state, struct msm_dsi_phy *phy)861{862	msm_disp_snapshot_add_block(disp_state,863			phy->base_size, phy->base,864			"dsi%d_phy", phy->id);865 866	/* Do not try accessing PLL registers if it is switched off */867	if (phy->pll_on)868		msm_disp_snapshot_add_block(disp_state,869			phy->pll_size, phy->pll_base,870			"dsi%d_pll", phy->id);871 872	if (phy->lane_base)873		msm_disp_snapshot_add_block(disp_state,874			phy->lane_size, phy->lane_base,875			"dsi%d_lane", phy->id);876 877	if (phy->reg_base)878		msm_disp_snapshot_add_block(disp_state,879			phy->reg_size, phy->reg_base,880			"dsi%d_reg", phy->id);881}882