xref: /linux/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1ec17f034SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0
2c1c026dbSPhilippe CORNU /*
3c1c026dbSPhilippe CORNU  * Copyright (C) STMicroelectronics SA 2017
4c1c026dbSPhilippe CORNU  *
5c1c026dbSPhilippe CORNU  * Authors: Philippe Cornu <philippe.cornu@st.com>
6c1c026dbSPhilippe CORNU  *          Yannick Fertre <yannick.fertre@st.com>
7c1c026dbSPhilippe CORNU  */
8c1c026dbSPhilippe CORNU 
9c1c026dbSPhilippe CORNU #include <linux/clk.h>
10*185f99b6SRaphael Gallais-Pou #include <linux/clk-provider.h>
11c1c026dbSPhilippe CORNU #include <linux/iopoll.h>
12*185f99b6SRaphael Gallais-Pou #include <linux/kernel.h>
1399a93888SSam Ravnborg #include <linux/mod_devicetable.h>
14c1c026dbSPhilippe CORNU #include <linux/module.h>
1599a93888SSam Ravnborg #include <linux/platform_device.h>
16b0e83c2cSYannick Fertre #include <linux/pm_runtime.h>
17f569aa9bSYannick Fertré #include <linux/regulator/consumer.h>
1899a93888SSam Ravnborg 
19c1c026dbSPhilippe CORNU #include <video/mipi_display.h>
20c1c026dbSPhilippe CORNU 
2199a93888SSam Ravnborg #include <drm/bridge/dw_mipi_dsi.h>
2299a93888SSam Ravnborg #include <drm/drm_mipi_dsi.h>
2399a93888SSam Ravnborg #include <drm/drm_print.h>
2499a93888SSam Ravnborg 
25023f3489SPhilippe CORNU #define HWVER_130			0x31333000	/* IP version 1.30 */
26023f3489SPhilippe CORNU #define HWVER_131			0x31333100	/* IP version 1.31 */
27023f3489SPhilippe CORNU 
28023f3489SPhilippe CORNU /* DSI digital registers & bit definitions */
29023f3489SPhilippe CORNU #define DSI_VERSION			0x00
30023f3489SPhilippe CORNU #define VERSION				GENMASK(31, 8)
31023f3489SPhilippe CORNU 
32023f3489SPhilippe CORNU /* DSI wrapper registers & bit definitions */
33c1c026dbSPhilippe CORNU /* Note: registers are named as in the Reference Manual */
34c1c026dbSPhilippe CORNU #define DSI_WCFGR	0x0400		/* Wrapper ConFiGuration Reg */
35c1c026dbSPhilippe CORNU #define WCFGR_DSIM	BIT(0)		/* DSI Mode */
36c1c026dbSPhilippe CORNU #define WCFGR_COLMUX	GENMASK(3, 1)	/* COLor MUltipleXing */
37c1c026dbSPhilippe CORNU 
38c1c026dbSPhilippe CORNU #define DSI_WCR		0x0404		/* Wrapper Control Reg */
39c1c026dbSPhilippe CORNU #define WCR_DSIEN	BIT(3)		/* DSI ENable */
40c1c026dbSPhilippe CORNU 
41c1c026dbSPhilippe CORNU #define DSI_WISR	0x040C		/* Wrapper Interrupt and Status Reg */
42c1c026dbSPhilippe CORNU #define WISR_PLLLS	BIT(8)		/* PLL Lock Status */
43c1c026dbSPhilippe CORNU #define WISR_RRS	BIT(12)		/* Regulator Ready Status */
44c1c026dbSPhilippe CORNU 
45c1c026dbSPhilippe CORNU #define DSI_WPCR0	0x0418		/* Wrapper Phy Conf Reg 0 */
46c1c026dbSPhilippe CORNU #define WPCR0_UIX4	GENMASK(5, 0)	/* Unit Interval X 4 */
47c1c026dbSPhilippe CORNU #define WPCR0_TDDL	BIT(16)		/* Turn Disable Data Lanes */
48c1c026dbSPhilippe CORNU 
49c1c026dbSPhilippe CORNU #define DSI_WRPCR	0x0430		/* Wrapper Regulator & Pll Ctrl Reg */
50c1c026dbSPhilippe CORNU #define WRPCR_PLLEN	BIT(0)		/* PLL ENable */
51c1c026dbSPhilippe CORNU #define WRPCR_NDIV	GENMASK(8, 2)	/* pll loop DIVision Factor */
52c1c026dbSPhilippe CORNU #define WRPCR_IDF	GENMASK(14, 11)	/* pll Input Division Factor */
53c1c026dbSPhilippe CORNU #define WRPCR_ODF	GENMASK(17, 16)	/* pll Output Division Factor */
54c1c026dbSPhilippe CORNU #define WRPCR_REGEN	BIT(24)		/* REGulator ENable */
55c1c026dbSPhilippe CORNU #define WRPCR_BGREN	BIT(28)		/* BandGap Reference ENable */
56c1c026dbSPhilippe CORNU #define IDF_MIN		1
57c1c026dbSPhilippe CORNU #define IDF_MAX		7
58c1c026dbSPhilippe CORNU #define NDIV_MIN	10
59c1c026dbSPhilippe CORNU #define NDIV_MAX	125
60c1c026dbSPhilippe CORNU #define ODF_MIN		1
61c1c026dbSPhilippe CORNU #define ODF_MAX		8
62c1c026dbSPhilippe CORNU 
63c1c026dbSPhilippe CORNU /* dsi color format coding according to the datasheet */
64c1c026dbSPhilippe CORNU enum dsi_color {
65c1c026dbSPhilippe CORNU 	DSI_RGB565_CONF1,
66c1c026dbSPhilippe CORNU 	DSI_RGB565_CONF2,
67c1c026dbSPhilippe CORNU 	DSI_RGB565_CONF3,
68c1c026dbSPhilippe CORNU 	DSI_RGB666_CONF1,
69c1c026dbSPhilippe CORNU 	DSI_RGB666_CONF2,
70c1c026dbSPhilippe CORNU 	DSI_RGB888,
71c1c026dbSPhilippe CORNU };
72c1c026dbSPhilippe CORNU 
73c1c026dbSPhilippe CORNU #define LANE_MIN_KBPS	31250
74c1c026dbSPhilippe CORNU #define LANE_MAX_KBPS	500000
75c1c026dbSPhilippe CORNU 
76c1c026dbSPhilippe CORNU /* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */
77c1c026dbSPhilippe CORNU #define SLEEP_US	1000
78c1c026dbSPhilippe CORNU #define TIMEOUT_US	200000
79c1c026dbSPhilippe CORNU 
80c1c026dbSPhilippe CORNU struct dw_mipi_dsi_stm {
81c1c026dbSPhilippe CORNU 	void __iomem *base;
82*185f99b6SRaphael Gallais-Pou 	struct device *dev;
83c1c026dbSPhilippe CORNU 	struct clk *pllref_clk;
84b0e83c2cSYannick Fertre 	struct clk *pclk;
85*185f99b6SRaphael Gallais-Pou 	struct clk_hw txbyte_clk;
868242ecbdSBrian Norris 	struct dw_mipi_dsi *dsi;
87*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_plat_data pdata;
88023f3489SPhilippe CORNU 	u32 hw_version;
89023f3489SPhilippe CORNU 	int lane_min_kbps;
90023f3489SPhilippe CORNU 	int lane_max_kbps;
91f569aa9bSYannick Fertré 	struct regulator *vdd_supply;
92c1c026dbSPhilippe CORNU };
93c1c026dbSPhilippe CORNU 
dsi_write(struct dw_mipi_dsi_stm * dsi,u32 reg,u32 val)94c1c026dbSPhilippe CORNU static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
95c1c026dbSPhilippe CORNU {
96c1c026dbSPhilippe CORNU 	writel(val, dsi->base + reg);
97c1c026dbSPhilippe CORNU }
98c1c026dbSPhilippe CORNU 
dsi_read(struct dw_mipi_dsi_stm * dsi,u32 reg)99c1c026dbSPhilippe CORNU static inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg)
100c1c026dbSPhilippe CORNU {
101c1c026dbSPhilippe CORNU 	return readl(dsi->base + reg);
102c1c026dbSPhilippe CORNU }
103c1c026dbSPhilippe CORNU 
dsi_set(struct dw_mipi_dsi_stm * dsi,u32 reg,u32 mask)104c1c026dbSPhilippe CORNU static inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
105c1c026dbSPhilippe CORNU {
106c1c026dbSPhilippe CORNU 	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
107c1c026dbSPhilippe CORNU }
108c1c026dbSPhilippe CORNU 
dsi_clear(struct dw_mipi_dsi_stm * dsi,u32 reg,u32 mask)109c1c026dbSPhilippe CORNU static inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
110c1c026dbSPhilippe CORNU {
111c1c026dbSPhilippe CORNU 	dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask);
112c1c026dbSPhilippe CORNU }
113c1c026dbSPhilippe CORNU 
dsi_update_bits(struct dw_mipi_dsi_stm * dsi,u32 reg,u32 mask,u32 val)114c1c026dbSPhilippe CORNU static inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg,
115c1c026dbSPhilippe CORNU 				   u32 mask, u32 val)
116c1c026dbSPhilippe CORNU {
117c1c026dbSPhilippe CORNU 	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
118c1c026dbSPhilippe CORNU }
119c1c026dbSPhilippe CORNU 
dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)120c1c026dbSPhilippe CORNU static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
121c1c026dbSPhilippe CORNU {
122c1c026dbSPhilippe CORNU 	switch (fmt) {
123c1c026dbSPhilippe CORNU 	case MIPI_DSI_FMT_RGB888:
124c1c026dbSPhilippe CORNU 		return DSI_RGB888;
125c1c026dbSPhilippe CORNU 	case MIPI_DSI_FMT_RGB666:
126c1c026dbSPhilippe CORNU 		return DSI_RGB666_CONF2;
127c1c026dbSPhilippe CORNU 	case MIPI_DSI_FMT_RGB666_PACKED:
128c1c026dbSPhilippe CORNU 		return DSI_RGB666_CONF1;
129c1c026dbSPhilippe CORNU 	case MIPI_DSI_FMT_RGB565:
130c1c026dbSPhilippe CORNU 		return DSI_RGB565_CONF1;
131c1c026dbSPhilippe CORNU 	default:
132c1c026dbSPhilippe CORNU 		DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n");
133c1c026dbSPhilippe CORNU 	}
134c1c026dbSPhilippe CORNU 	return DSI_RGB888;
135c1c026dbSPhilippe CORNU }
136c1c026dbSPhilippe CORNU 
dsi_pll_get_clkout_khz(int clkin_khz,int idf,int ndiv,int odf)137c1c026dbSPhilippe CORNU static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
138c1c026dbSPhilippe CORNU {
1393ff558e7SArnd Bergmann 	int divisor = idf * odf;
140c1c026dbSPhilippe CORNU 
1413ff558e7SArnd Bergmann 	/* prevent from division by 0 */
1423ff558e7SArnd Bergmann 	if (!divisor)
143c1c026dbSPhilippe CORNU 		return 0;
1443ff558e7SArnd Bergmann 
1453ff558e7SArnd Bergmann 	return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
146c1c026dbSPhilippe CORNU }
147c1c026dbSPhilippe CORNU 
dsi_pll_get_params(struct dw_mipi_dsi_stm * dsi,int clkin_khz,int clkout_khz,int * idf,int * ndiv,int * odf)148023f3489SPhilippe CORNU static int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi,
149023f3489SPhilippe CORNU 			      int clkin_khz, int clkout_khz,
150c1c026dbSPhilippe CORNU 			      int *idf, int *ndiv, int *odf)
151c1c026dbSPhilippe CORNU {
152c1c026dbSPhilippe CORNU 	int i, o, n, n_min, n_max;
153c1c026dbSPhilippe CORNU 	int fvco_min, fvco_max, delta, best_delta; /* all in khz */
154c1c026dbSPhilippe CORNU 
155c1c026dbSPhilippe CORNU 	/* Early checks preventing division by 0 & odd results */
1560163d1f6SPhilippe CORNU 	if (clkin_khz <= 0 || clkout_khz <= 0)
157c1c026dbSPhilippe CORNU 		return -EINVAL;
158c1c026dbSPhilippe CORNU 
159023f3489SPhilippe CORNU 	fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX;
160023f3489SPhilippe CORNU 	fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN;
161c1c026dbSPhilippe CORNU 
162c1c026dbSPhilippe CORNU 	best_delta = 1000000; /* big started value (1000000khz) */
163c1c026dbSPhilippe CORNU 
164c1c026dbSPhilippe CORNU 	for (i = IDF_MIN; i <= IDF_MAX; i++) {
165c1c026dbSPhilippe CORNU 		/* Compute ndiv range according to Fvco */
166c1c026dbSPhilippe CORNU 		n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
167c1c026dbSPhilippe CORNU 		n_max = (fvco_max * i) / (2 * clkin_khz);
168c1c026dbSPhilippe CORNU 
169c1c026dbSPhilippe CORNU 		/* No need to continue idf loop if we reach ndiv max */
170c1c026dbSPhilippe CORNU 		if (n_min >= NDIV_MAX)
171c1c026dbSPhilippe CORNU 			break;
172c1c026dbSPhilippe CORNU 
173c1c026dbSPhilippe CORNU 		/* Clamp ndiv to valid values */
174c1c026dbSPhilippe CORNU 		if (n_min < NDIV_MIN)
175c1c026dbSPhilippe CORNU 			n_min = NDIV_MIN;
176c1c026dbSPhilippe CORNU 		if (n_max > NDIV_MAX)
177c1c026dbSPhilippe CORNU 			n_max = NDIV_MAX;
178c1c026dbSPhilippe CORNU 
179c1c026dbSPhilippe CORNU 		for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
180c1c026dbSPhilippe CORNU 			n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
181c1c026dbSPhilippe CORNU 			/* Check ndiv according to vco range */
1820163d1f6SPhilippe CORNU 			if (n < n_min || n > n_max)
183c1c026dbSPhilippe CORNU 				continue;
184c1c026dbSPhilippe CORNU 			/* Check if new delta is better & saves parameters */
185c1c026dbSPhilippe CORNU 			delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
186c1c026dbSPhilippe CORNU 				clkout_khz;
187c1c026dbSPhilippe CORNU 			if (delta < 0)
188c1c026dbSPhilippe CORNU 				delta = -delta;
189c1c026dbSPhilippe CORNU 			if (delta < best_delta) {
190c1c026dbSPhilippe CORNU 				*idf = i;
191c1c026dbSPhilippe CORNU 				*ndiv = n;
192c1c026dbSPhilippe CORNU 				*odf = o;
193c1c026dbSPhilippe CORNU 				best_delta = delta;
194c1c026dbSPhilippe CORNU 			}
195c1c026dbSPhilippe CORNU 			/* fast return in case of "perfect result" */
196c1c026dbSPhilippe CORNU 			if (!delta)
197c1c026dbSPhilippe CORNU 				return 0;
198c1c026dbSPhilippe CORNU 		}
199c1c026dbSPhilippe CORNU 	}
200c1c026dbSPhilippe CORNU 
201c1c026dbSPhilippe CORNU 	return 0;
202c1c026dbSPhilippe CORNU }
203c1c026dbSPhilippe CORNU 
204*185f99b6SRaphael Gallais-Pou #define clk_to_dw_mipi_dsi_stm(clk) \
205*185f99b6SRaphael Gallais-Pou 	container_of(clk, struct dw_mipi_dsi_stm, txbyte_clk)
206*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_disable(struct clk_hw * clk)207*185f99b6SRaphael Gallais-Pou static void dw_mipi_dsi_clk_disable(struct clk_hw *clk)
208c1c026dbSPhilippe CORNU {
209*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk);
210*185f99b6SRaphael Gallais-Pou 
211*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
212*185f99b6SRaphael Gallais-Pou 
213*185f99b6SRaphael Gallais-Pou 	/* Disable the DSI PLL */
214*185f99b6SRaphael Gallais-Pou 	dsi_clear(dsi, DSI_WRPCR, WRPCR_PLLEN);
215*185f99b6SRaphael Gallais-Pou 
216*185f99b6SRaphael Gallais-Pou 	/* Disable the regulator */
217*185f99b6SRaphael Gallais-Pou 	dsi_clear(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
218*185f99b6SRaphael Gallais-Pou }
219*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_enable(struct clk_hw * clk)220*185f99b6SRaphael Gallais-Pou static int dw_mipi_dsi_clk_enable(struct clk_hw *clk)
221*185f99b6SRaphael Gallais-Pou {
222*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk);
223c1c026dbSPhilippe CORNU 	u32 val;
224c1c026dbSPhilippe CORNU 	int ret;
225c1c026dbSPhilippe CORNU 
226*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
227*185f99b6SRaphael Gallais-Pou 
228c1c026dbSPhilippe CORNU 	/* Enable the regulator */
229c1c026dbSPhilippe CORNU 	dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
230*185f99b6SRaphael Gallais-Pou 	ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_RRS,
231c1c026dbSPhilippe CORNU 					SLEEP_US, TIMEOUT_US);
232c1c026dbSPhilippe CORNU 	if (ret)
233c1c026dbSPhilippe CORNU 		DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n");
234c1c026dbSPhilippe CORNU 
235c1c026dbSPhilippe CORNU 	/* Enable the DSI PLL & wait for its lock */
236c1c026dbSPhilippe CORNU 	dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
237*185f99b6SRaphael Gallais-Pou 	ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
238c1c026dbSPhilippe CORNU 					SLEEP_US, TIMEOUT_US);
239c1c026dbSPhilippe CORNU 	if (ret)
240c1c026dbSPhilippe CORNU 		DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n");
241c1c026dbSPhilippe CORNU 
242ee7668bcSYannick Fertré 	return 0;
243ee7668bcSYannick Fertré }
244ee7668bcSYannick Fertré 
dw_mipi_dsi_clk_is_enabled(struct clk_hw * hw)245*185f99b6SRaphael Gallais-Pou static int dw_mipi_dsi_clk_is_enabled(struct clk_hw *hw)
246*185f99b6SRaphael Gallais-Pou {
247*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
248*185f99b6SRaphael Gallais-Pou 
249*185f99b6SRaphael Gallais-Pou 	return dsi_read(dsi, DSI_WRPCR) & WRPCR_PLLEN;
250*185f99b6SRaphael Gallais-Pou }
251*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)252*185f99b6SRaphael Gallais-Pou static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw,
253*185f99b6SRaphael Gallais-Pou 						 unsigned long parent_rate)
254*185f99b6SRaphael Gallais-Pou {
255*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
256*185f99b6SRaphael Gallais-Pou 	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
257*185f99b6SRaphael Gallais-Pou 	u32 val;
258*185f99b6SRaphael Gallais-Pou 
259*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
260*185f99b6SRaphael Gallais-Pou 
261*185f99b6SRaphael Gallais-Pou 	pll_in_khz = (unsigned int)(parent_rate / 1000);
262*185f99b6SRaphael Gallais-Pou 
263*185f99b6SRaphael Gallais-Pou 	val = dsi_read(dsi, DSI_WRPCR);
264*185f99b6SRaphael Gallais-Pou 
265*185f99b6SRaphael Gallais-Pou 	idf = (val & WRPCR_IDF) >> 11;
266*185f99b6SRaphael Gallais-Pou 	if (!idf)
267*185f99b6SRaphael Gallais-Pou 		idf = 1;
268*185f99b6SRaphael Gallais-Pou 	ndiv = (val & WRPCR_NDIV) >> 2;
269*185f99b6SRaphael Gallais-Pou 	odf = int_pow(2, (val & WRPCR_ODF) >> 16);
270*185f99b6SRaphael Gallais-Pou 
271*185f99b6SRaphael Gallais-Pou 	/* Get the adjusted pll out value */
272*185f99b6SRaphael Gallais-Pou 	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
273*185f99b6SRaphael Gallais-Pou 
274*185f99b6SRaphael Gallais-Pou 	return (unsigned long)pll_out_khz * 1000;
275*185f99b6SRaphael Gallais-Pou }
276*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)277*185f99b6SRaphael Gallais-Pou static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
278*185f99b6SRaphael Gallais-Pou 				       unsigned long *parent_rate)
279*185f99b6SRaphael Gallais-Pou {
280*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
281*185f99b6SRaphael Gallais-Pou 	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
282*185f99b6SRaphael Gallais-Pou 	int ret;
283*185f99b6SRaphael Gallais-Pou 
284*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
285*185f99b6SRaphael Gallais-Pou 
286*185f99b6SRaphael Gallais-Pou 	pll_in_khz = (unsigned int)(*parent_rate / 1000);
287*185f99b6SRaphael Gallais-Pou 
288*185f99b6SRaphael Gallais-Pou 	/* Compute best pll parameters */
289*185f99b6SRaphael Gallais-Pou 	idf = 0;
290*185f99b6SRaphael Gallais-Pou 	ndiv = 0;
291*185f99b6SRaphael Gallais-Pou 	odf = 0;
292*185f99b6SRaphael Gallais-Pou 
293*185f99b6SRaphael Gallais-Pou 	ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000,
294*185f99b6SRaphael Gallais-Pou 				 &idf, &ndiv, &odf);
295*185f99b6SRaphael Gallais-Pou 	if (ret)
296*185f99b6SRaphael Gallais-Pou 		DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
297*185f99b6SRaphael Gallais-Pou 
298*185f99b6SRaphael Gallais-Pou 	/* Get the adjusted pll out value */
299*185f99b6SRaphael Gallais-Pou 	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
300*185f99b6SRaphael Gallais-Pou 
301*185f99b6SRaphael Gallais-Pou 	return pll_out_khz * 1000;
302*185f99b6SRaphael Gallais-Pou }
303*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)304*185f99b6SRaphael Gallais-Pou static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
305*185f99b6SRaphael Gallais-Pou 				    unsigned long parent_rate)
306*185f99b6SRaphael Gallais-Pou {
307*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
308*185f99b6SRaphael Gallais-Pou 	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
309*185f99b6SRaphael Gallais-Pou 	int ret;
310*185f99b6SRaphael Gallais-Pou 	u32 val;
311*185f99b6SRaphael Gallais-Pou 
312*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
313*185f99b6SRaphael Gallais-Pou 
314*185f99b6SRaphael Gallais-Pou 	pll_in_khz = (unsigned int)(parent_rate / 1000);
315*185f99b6SRaphael Gallais-Pou 
316*185f99b6SRaphael Gallais-Pou 	/* Compute best pll parameters */
317*185f99b6SRaphael Gallais-Pou 	idf = 0;
318*185f99b6SRaphael Gallais-Pou 	ndiv = 0;
319*185f99b6SRaphael Gallais-Pou 	odf = 0;
320*185f99b6SRaphael Gallais-Pou 
321*185f99b6SRaphael Gallais-Pou 	ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, &idf, &ndiv, &odf);
322*185f99b6SRaphael Gallais-Pou 	if (ret)
323*185f99b6SRaphael Gallais-Pou 		DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
324*185f99b6SRaphael Gallais-Pou 
325*185f99b6SRaphael Gallais-Pou 	/* Get the adjusted pll out value */
326*185f99b6SRaphael Gallais-Pou 	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
327*185f99b6SRaphael Gallais-Pou 
328*185f99b6SRaphael Gallais-Pou 	/* Set the PLL division factors */
329*185f99b6SRaphael Gallais-Pou 	dsi_update_bits(dsi, DSI_WRPCR,	WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
330*185f99b6SRaphael Gallais-Pou 			(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
331*185f99b6SRaphael Gallais-Pou 
332*185f99b6SRaphael Gallais-Pou 	/* Compute uix4 & set the bit period in high-speed mode */
333*185f99b6SRaphael Gallais-Pou 	val = 4000000 / pll_out_khz;
334*185f99b6SRaphael Gallais-Pou 	dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
335*185f99b6SRaphael Gallais-Pou 
336*185f99b6SRaphael Gallais-Pou 	return 0;
337*185f99b6SRaphael Gallais-Pou }
338*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_unregister(void * data)339*185f99b6SRaphael Gallais-Pou static void dw_mipi_dsi_clk_unregister(void *data)
340*185f99b6SRaphael Gallais-Pou {
341*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = data;
342*185f99b6SRaphael Gallais-Pou 
343*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("\n");
344*185f99b6SRaphael Gallais-Pou 
345*185f99b6SRaphael Gallais-Pou 	of_clk_del_provider(dsi->dev->of_node);
346*185f99b6SRaphael Gallais-Pou 	clk_hw_unregister(&dsi->txbyte_clk);
347*185f99b6SRaphael Gallais-Pou }
348*185f99b6SRaphael Gallais-Pou 
349*185f99b6SRaphael Gallais-Pou static const struct clk_ops dw_mipi_dsi_stm_clk_ops = {
350*185f99b6SRaphael Gallais-Pou 	.enable = dw_mipi_dsi_clk_enable,
351*185f99b6SRaphael Gallais-Pou 	.disable = dw_mipi_dsi_clk_disable,
352*185f99b6SRaphael Gallais-Pou 	.is_enabled = dw_mipi_dsi_clk_is_enabled,
353*185f99b6SRaphael Gallais-Pou 	.recalc_rate = dw_mipi_dsi_clk_recalc_rate,
354*185f99b6SRaphael Gallais-Pou 	.round_rate = dw_mipi_dsi_clk_round_rate,
355*185f99b6SRaphael Gallais-Pou 	.set_rate = dw_mipi_dsi_clk_set_rate,
356*185f99b6SRaphael Gallais-Pou };
357*185f99b6SRaphael Gallais-Pou 
358*185f99b6SRaphael Gallais-Pou static struct clk_init_data cdata_init = {
359*185f99b6SRaphael Gallais-Pou 	.name = "ck_dsi_phy",
360*185f99b6SRaphael Gallais-Pou 	.ops = &dw_mipi_dsi_stm_clk_ops,
361*185f99b6SRaphael Gallais-Pou 	.parent_names = (const char * []) {"ck_hse"},
362*185f99b6SRaphael Gallais-Pou 	.num_parents = 1,
363*185f99b6SRaphael Gallais-Pou };
364*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_clk_register(struct dw_mipi_dsi_stm * dsi,struct device * dev)365*185f99b6SRaphael Gallais-Pou static int dw_mipi_dsi_clk_register(struct dw_mipi_dsi_stm *dsi,
366*185f99b6SRaphael Gallais-Pou 				    struct device *dev)
367*185f99b6SRaphael Gallais-Pou {
368*185f99b6SRaphael Gallais-Pou 	struct device_node *node = dev->of_node;
369*185f99b6SRaphael Gallais-Pou 	int ret;
370*185f99b6SRaphael Gallais-Pou 
371*185f99b6SRaphael Gallais-Pou 	DRM_DEBUG_DRIVER("Registering clk\n");
372*185f99b6SRaphael Gallais-Pou 
373*185f99b6SRaphael Gallais-Pou 	dsi->txbyte_clk.init = &cdata_init;
374*185f99b6SRaphael Gallais-Pou 
375*185f99b6SRaphael Gallais-Pou 	ret = clk_hw_register(dev, &dsi->txbyte_clk);
376*185f99b6SRaphael Gallais-Pou 	if (ret)
377*185f99b6SRaphael Gallais-Pou 		return ret;
378*185f99b6SRaphael Gallais-Pou 
379*185f99b6SRaphael Gallais-Pou 	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get,
380*185f99b6SRaphael Gallais-Pou 				     &dsi->txbyte_clk);
381*185f99b6SRaphael Gallais-Pou 	if (ret)
382*185f99b6SRaphael Gallais-Pou 		clk_hw_unregister(&dsi->txbyte_clk);
383*185f99b6SRaphael Gallais-Pou 
384*185f99b6SRaphael Gallais-Pou 	return ret;
385*185f99b6SRaphael Gallais-Pou }
386*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_phy_init(void * priv_data)387*185f99b6SRaphael Gallais-Pou static int dw_mipi_dsi_phy_init(void *priv_data)
388*185f99b6SRaphael Gallais-Pou {
389*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = priv_data;
390*185f99b6SRaphael Gallais-Pou 	int ret;
391*185f99b6SRaphael Gallais-Pou 
392*185f99b6SRaphael Gallais-Pou 	ret = clk_prepare_enable(dsi->txbyte_clk.clk);
393*185f99b6SRaphael Gallais-Pou 	return ret;
394*185f99b6SRaphael Gallais-Pou }
395*185f99b6SRaphael Gallais-Pou 
dw_mipi_dsi_phy_power_on(void * priv_data)396ee7668bcSYannick Fertré static void dw_mipi_dsi_phy_power_on(void *priv_data)
397ee7668bcSYannick Fertré {
398ee7668bcSYannick Fertré 	struct dw_mipi_dsi_stm *dsi = priv_data;
399ee7668bcSYannick Fertré 
400ee7668bcSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
401ee7668bcSYannick Fertré 
402c1c026dbSPhilippe CORNU 	/* Enable the DSI wrapper */
403c1c026dbSPhilippe CORNU 	dsi_set(dsi, DSI_WCR, WCR_DSIEN);
404ee7668bcSYannick Fertré }
405c1c026dbSPhilippe CORNU 
dw_mipi_dsi_phy_power_off(void * priv_data)406ee7668bcSYannick Fertré static void dw_mipi_dsi_phy_power_off(void *priv_data)
407ee7668bcSYannick Fertré {
408ee7668bcSYannick Fertré 	struct dw_mipi_dsi_stm *dsi = priv_data;
409ee7668bcSYannick Fertré 
410ee7668bcSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
411ee7668bcSYannick Fertré 
412*185f99b6SRaphael Gallais-Pou 	clk_disable_unprepare(dsi->txbyte_clk.clk);
413*185f99b6SRaphael Gallais-Pou 
414ee7668bcSYannick Fertré 	/* Disable the DSI wrapper */
415ee7668bcSYannick Fertré 	dsi_clear(dsi, DSI_WCR, WCR_DSIEN);
416c1c026dbSPhilippe CORNU }
417c1c026dbSPhilippe CORNU 
418c1c026dbSPhilippe CORNU static int
dw_mipi_dsi_get_lane_mbps(void * priv_data,const struct drm_display_mode * mode,unsigned long mode_flags,u32 lanes,u32 format,unsigned int * lane_mbps)41963f8f3baSLaurent Pinchart dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
420c1c026dbSPhilippe CORNU 			  unsigned long mode_flags, u32 lanes, u32 format,
421c1c026dbSPhilippe CORNU 			  unsigned int *lane_mbps)
422c1c026dbSPhilippe CORNU {
423c1c026dbSPhilippe CORNU 	struct dw_mipi_dsi_stm *dsi = priv_data;
424*185f99b6SRaphael Gallais-Pou 	unsigned int pll_in_khz, pll_out_khz;
425c1c026dbSPhilippe CORNU 	int ret, bpp;
426c1c026dbSPhilippe CORNU 
427c1c026dbSPhilippe CORNU 	pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
428c1c026dbSPhilippe CORNU 
429c1c026dbSPhilippe CORNU 	/* Compute requested pll out */
430c1c026dbSPhilippe CORNU 	bpp = mipi_dsi_pixel_format_to_bpp(format);
431c1c026dbSPhilippe CORNU 	pll_out_khz = mode->clock * bpp / lanes;
4321e696204SYannick Fertré 
433c1c026dbSPhilippe CORNU 	/* Add 20% to pll out to be higher than pixel bw (burst mode only) */
4341e696204SYannick Fertré 	if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
435c1c026dbSPhilippe CORNU 		pll_out_khz = (pll_out_khz * 12) / 10;
4361e696204SYannick Fertré 
437023f3489SPhilippe CORNU 	if (pll_out_khz > dsi->lane_max_kbps) {
438023f3489SPhilippe CORNU 		pll_out_khz = dsi->lane_max_kbps;
439c1c026dbSPhilippe CORNU 		DRM_WARN("Warning max phy mbps is used\n");
440c1c026dbSPhilippe CORNU 	}
441023f3489SPhilippe CORNU 	if (pll_out_khz < dsi->lane_min_kbps) {
442023f3489SPhilippe CORNU 		pll_out_khz = dsi->lane_min_kbps;
443c1c026dbSPhilippe CORNU 		DRM_WARN("Warning min phy mbps is used\n");
444c1c026dbSPhilippe CORNU 	}
445c1c026dbSPhilippe CORNU 
446*185f99b6SRaphael Gallais-Pou 	ret = clk_set_rate((dsi->txbyte_clk.clk), pll_out_khz * 1000);
447c1c026dbSPhilippe CORNU 	if (ret)
448*185f99b6SRaphael Gallais-Pou 		DRM_DEBUG_DRIVER("ERROR Could not set rate of %d to %s clk->name",
449*185f99b6SRaphael Gallais-Pou 				 pll_out_khz, clk_hw_get_name(&dsi->txbyte_clk));
450c1c026dbSPhilippe CORNU 
451c1c026dbSPhilippe CORNU 	/* Select video mode by resetting DSIM bit */
452c1c026dbSPhilippe CORNU 	dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
453c1c026dbSPhilippe CORNU 
454c1c026dbSPhilippe CORNU 	/* Select the color coding */
455c1c026dbSPhilippe CORNU 	dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
456c1c026dbSPhilippe CORNU 			dsi_color_from_mipi(format) << 1);
457c1c026dbSPhilippe CORNU 
458c1c026dbSPhilippe CORNU 	*lane_mbps = pll_out_khz / 1000;
459c1c026dbSPhilippe CORNU 
460c1c026dbSPhilippe CORNU 	DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n",
461c1c026dbSPhilippe CORNU 			 pll_in_khz, pll_out_khz, *lane_mbps);
462c1c026dbSPhilippe CORNU 
463c1c026dbSPhilippe CORNU 	return 0;
464c1c026dbSPhilippe CORNU }
465c1c026dbSPhilippe CORNU 
4665cc4e71fSAntonio Borneo #define DSI_PHY_DELAY(fp, vp, mbps) DIV_ROUND_UP((fp) * (mbps) + 1000 * (vp), 8000)
4675cc4e71fSAntonio Borneo 
46825ed8aebSHeiko Stuebner static int
dw_mipi_dsi_phy_get_timing(void * priv_data,unsigned int lane_mbps,struct dw_mipi_dsi_dphy_timing * timing)46925ed8aebSHeiko Stuebner dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
47025ed8aebSHeiko Stuebner 			   struct dw_mipi_dsi_dphy_timing *timing)
47125ed8aebSHeiko Stuebner {
4725cc4e71fSAntonio Borneo 	/*
4735cc4e71fSAntonio Borneo 	 * From STM32MP157 datasheet, valid for STM32F469, STM32F7x9, STM32H747
4745cc4e71fSAntonio Borneo 	 * phy_clkhs2lp_time = (272+136*UI)/(8*UI)
4755cc4e71fSAntonio Borneo 	 * phy_clklp2hs_time = (512+40*UI)/(8*UI)
4765cc4e71fSAntonio Borneo 	 * phy_hs2lp_time = (192+64*UI)/(8*UI)
4775cc4e71fSAntonio Borneo 	 * phy_lp2hs_time = (256+32*UI)/(8*UI)
4785cc4e71fSAntonio Borneo 	 */
4795cc4e71fSAntonio Borneo 	timing->clk_hs2lp = DSI_PHY_DELAY(272, 136, lane_mbps);
4805cc4e71fSAntonio Borneo 	timing->clk_lp2hs = DSI_PHY_DELAY(512, 40, lane_mbps);
4815cc4e71fSAntonio Borneo 	timing->data_hs2lp = DSI_PHY_DELAY(192, 64, lane_mbps);
4825cc4e71fSAntonio Borneo 	timing->data_lp2hs = DSI_PHY_DELAY(256, 32, lane_mbps);
48325ed8aebSHeiko Stuebner 
48425ed8aebSHeiko Stuebner 	return 0;
48525ed8aebSHeiko Stuebner }
48625ed8aebSHeiko Stuebner 
487e01356d1SAntonio Borneo #define CLK_TOLERANCE_HZ 50
488e01356d1SAntonio Borneo 
489e01356d1SAntonio Borneo static enum drm_mode_status
dw_mipi_dsi_stm_mode_valid(void * priv_data,const struct drm_display_mode * mode,unsigned long mode_flags,u32 lanes,u32 format)490e01356d1SAntonio Borneo dw_mipi_dsi_stm_mode_valid(void *priv_data,
491e01356d1SAntonio Borneo 			   const struct drm_display_mode *mode,
492e01356d1SAntonio Borneo 			   unsigned long mode_flags, u32 lanes, u32 format)
493e01356d1SAntonio Borneo {
494e01356d1SAntonio Borneo 	struct dw_mipi_dsi_stm *dsi = priv_data;
495e01356d1SAntonio Borneo 	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
496e01356d1SAntonio Borneo 	int ret, bpp;
497e01356d1SAntonio Borneo 
498e01356d1SAntonio Borneo 	bpp = mipi_dsi_pixel_format_to_bpp(format);
499e01356d1SAntonio Borneo 	if (bpp < 0)
500e01356d1SAntonio Borneo 		return MODE_BAD;
501e01356d1SAntonio Borneo 
502e01356d1SAntonio Borneo 	/* Compute requested pll out */
503e01356d1SAntonio Borneo 	pll_out_khz = mode->clock * bpp / lanes;
504e01356d1SAntonio Borneo 
505e01356d1SAntonio Borneo 	if (pll_out_khz > dsi->lane_max_kbps)
506e01356d1SAntonio Borneo 		return MODE_CLOCK_HIGH;
507e01356d1SAntonio Borneo 
508e01356d1SAntonio Borneo 	if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
509e01356d1SAntonio Borneo 		/* Add 20% to pll out to be higher than pixel bw */
510e01356d1SAntonio Borneo 		pll_out_khz = (pll_out_khz * 12) / 10;
511e01356d1SAntonio Borneo 	} else {
512e01356d1SAntonio Borneo 		if (pll_out_khz < dsi->lane_min_kbps)
513e01356d1SAntonio Borneo 			return MODE_CLOCK_LOW;
514e01356d1SAntonio Borneo 	}
515e01356d1SAntonio Borneo 
516e01356d1SAntonio Borneo 	/* Compute best pll parameters */
517e01356d1SAntonio Borneo 	idf = 0;
518e01356d1SAntonio Borneo 	ndiv = 0;
519e01356d1SAntonio Borneo 	odf = 0;
520e01356d1SAntonio Borneo 	pll_in_khz = clk_get_rate(dsi->pllref_clk) / 1000;
521e01356d1SAntonio Borneo 	ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, &idf, &ndiv, &odf);
522e01356d1SAntonio Borneo 	if (ret) {
523e01356d1SAntonio Borneo 		DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
524e01356d1SAntonio Borneo 		return MODE_ERROR;
525e01356d1SAntonio Borneo 	}
526e01356d1SAntonio Borneo 
527e01356d1SAntonio Borneo 	if (!(mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) {
528e01356d1SAntonio Borneo 		unsigned int px_clock_hz, target_px_clock_hz, lane_mbps;
529e01356d1SAntonio Borneo 		int dsi_short_packet_size_px, hfp, hsync, hbp, delay_to_lp;
530e01356d1SAntonio Borneo 		struct dw_mipi_dsi_dphy_timing dphy_timing;
531e01356d1SAntonio Borneo 
532e01356d1SAntonio Borneo 		/* Get the adjusted pll out value */
533e01356d1SAntonio Borneo 		pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
534e01356d1SAntonio Borneo 
535e01356d1SAntonio Borneo 		px_clock_hz = DIV_ROUND_CLOSEST_ULL(1000ULL * pll_out_khz * lanes, bpp);
536e01356d1SAntonio Borneo 		target_px_clock_hz = mode->clock * 1000;
537e01356d1SAntonio Borneo 		/*
538e01356d1SAntonio Borneo 		 * Filter modes according to the clock value, particularly useful for
539e01356d1SAntonio Borneo 		 * hdmi modes that require precise pixel clocks.
540e01356d1SAntonio Borneo 		 */
541e01356d1SAntonio Borneo 		if (px_clock_hz < target_px_clock_hz - CLK_TOLERANCE_HZ ||
542e01356d1SAntonio Borneo 		    px_clock_hz > target_px_clock_hz + CLK_TOLERANCE_HZ)
543e01356d1SAntonio Borneo 			return MODE_CLOCK_RANGE;
544e01356d1SAntonio Borneo 
545e01356d1SAntonio Borneo 		/* sync packets are codes as DSI short packets (4 bytes) */
546e01356d1SAntonio Borneo 		dsi_short_packet_size_px = DIV_ROUND_UP(4 * BITS_PER_BYTE, bpp);
547e01356d1SAntonio Borneo 
548e01356d1SAntonio Borneo 		hfp = mode->hsync_start - mode->hdisplay;
549e01356d1SAntonio Borneo 		hsync = mode->hsync_end - mode->hsync_start;
550e01356d1SAntonio Borneo 		hbp = mode->htotal - mode->hsync_end;
551e01356d1SAntonio Borneo 
552e01356d1SAntonio Borneo 		/* hsync must be longer than 4 bytes HSS packets */
553e01356d1SAntonio Borneo 		if (hsync < dsi_short_packet_size_px)
554e01356d1SAntonio Borneo 			return MODE_HSYNC_NARROW;
555e01356d1SAntonio Borneo 
556e01356d1SAntonio Borneo 		if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
557e01356d1SAntonio Borneo 			/* HBP must be longer than 4 bytes HSE packets */
558e01356d1SAntonio Borneo 			if (hbp < dsi_short_packet_size_px)
559e01356d1SAntonio Borneo 				return MODE_HSYNC_NARROW;
560e01356d1SAntonio Borneo 			hbp -= dsi_short_packet_size_px;
561e01356d1SAntonio Borneo 		} else {
562e01356d1SAntonio Borneo 			/* With sync events HBP extends in the hsync */
563e01356d1SAntonio Borneo 			hbp += hsync - dsi_short_packet_size_px;
564e01356d1SAntonio Borneo 		}
565e01356d1SAntonio Borneo 
566e01356d1SAntonio Borneo 		lane_mbps = pll_out_khz / 1000;
567e01356d1SAntonio Borneo 		ret = dw_mipi_dsi_phy_get_timing(priv_data, lane_mbps, &dphy_timing);
568e01356d1SAntonio Borneo 		if (ret)
569e01356d1SAntonio Borneo 			return MODE_ERROR;
570e01356d1SAntonio Borneo 		/*
571e01356d1SAntonio Borneo 		 * In non-burst mode DSI has to enter in LP during HFP
572e01356d1SAntonio Borneo 		 * (horizontal front porch) or HBP (horizontal back porch) to
573e01356d1SAntonio Borneo 		 * resync with LTDC pixel clock.
574e01356d1SAntonio Borneo 		 */
575e01356d1SAntonio Borneo 		delay_to_lp = DIV_ROUND_UP((dphy_timing.data_hs2lp + dphy_timing.data_lp2hs) *
576e01356d1SAntonio Borneo 					   lanes * BITS_PER_BYTE, bpp);
577e01356d1SAntonio Borneo 		if (hfp < delay_to_lp && hbp < delay_to_lp)
578e01356d1SAntonio Borneo 			return MODE_HSYNC;
579e01356d1SAntonio Borneo 	}
580e01356d1SAntonio Borneo 
581e01356d1SAntonio Borneo 	return MODE_OK;
582e01356d1SAntonio Borneo }
583e01356d1SAntonio Borneo 
58489a15e6fSPhilippe CORNU static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
585c1c026dbSPhilippe CORNU 	.init = dw_mipi_dsi_phy_init,
586ee7668bcSYannick Fertré 	.power_on = dw_mipi_dsi_phy_power_on,
587ee7668bcSYannick Fertré 	.power_off = dw_mipi_dsi_phy_power_off,
588c1c026dbSPhilippe CORNU 	.get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
58925ed8aebSHeiko Stuebner 	.get_timing = dw_mipi_dsi_phy_get_timing,
590c1c026dbSPhilippe CORNU };
591c1c026dbSPhilippe CORNU 
592c1c026dbSPhilippe CORNU static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = {
593c1c026dbSPhilippe CORNU 	.max_data_lanes = 2,
594e01356d1SAntonio Borneo 	.mode_valid = dw_mipi_dsi_stm_mode_valid,
595c1c026dbSPhilippe CORNU 	.phy_ops = &dw_mipi_dsi_stm_phy_ops,
596c1c026dbSPhilippe CORNU };
597c1c026dbSPhilippe CORNU 
598c1c026dbSPhilippe CORNU static const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = {
599c1c026dbSPhilippe CORNU 	{ .compatible = "st,stm32-dsi", .data = &dw_mipi_dsi_stm_plat_data, },
600c1c026dbSPhilippe CORNU 	{ },
601c1c026dbSPhilippe CORNU };
602c1c026dbSPhilippe CORNU MODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids);
603c1c026dbSPhilippe CORNU 
dw_mipi_dsi_stm_probe(struct platform_device * pdev)604c1c026dbSPhilippe CORNU static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
605c1c026dbSPhilippe CORNU {
606c1c026dbSPhilippe CORNU 	struct device *dev = &pdev->dev;
607c1c026dbSPhilippe CORNU 	struct dw_mipi_dsi_stm *dsi;
608*185f99b6SRaphael Gallais-Pou 	const struct dw_mipi_dsi_plat_data *pdata = of_device_get_match_data(dev);
609c1c026dbSPhilippe CORNU 	int ret;
610c1c026dbSPhilippe CORNU 
611c1c026dbSPhilippe CORNU 	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
612c1c026dbSPhilippe CORNU 	if (!dsi)
613c1c026dbSPhilippe CORNU 		return -ENOMEM;
614c1c026dbSPhilippe CORNU 
61534235f54SYang Li 	dsi->base = devm_platform_ioremap_resource(pdev, 0);
616c1c026dbSPhilippe CORNU 	if (IS_ERR(dsi->base)) {
617f569aa9bSYannick Fertré 		ret = PTR_ERR(dsi->base);
618f569aa9bSYannick Fertré 		DRM_ERROR("Unable to get dsi registers %d\n", ret);
619f569aa9bSYannick Fertré 		return ret;
620f569aa9bSYannick Fertré 	}
621f569aa9bSYannick Fertré 
622f569aa9bSYannick Fertré 	dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi");
623f569aa9bSYannick Fertré 	if (IS_ERR(dsi->vdd_supply)) {
624f569aa9bSYannick Fertré 		ret = PTR_ERR(dsi->vdd_supply);
625edf20859SYannick Fertre 		dev_err_probe(dev, ret, "Failed to request regulator\n");
626f569aa9bSYannick Fertré 		return ret;
627f569aa9bSYannick Fertré 	}
628f569aa9bSYannick Fertré 
629f569aa9bSYannick Fertré 	ret = regulator_enable(dsi->vdd_supply);
630f569aa9bSYannick Fertré 	if (ret) {
631f569aa9bSYannick Fertré 		DRM_ERROR("Failed to enable regulator: %d\n", ret);
632f569aa9bSYannick Fertré 		return ret;
633c1c026dbSPhilippe CORNU 	}
634c1c026dbSPhilippe CORNU 
635c1c026dbSPhilippe CORNU 	dsi->pllref_clk = devm_clk_get(dev, "ref");
636c1c026dbSPhilippe CORNU 	if (IS_ERR(dsi->pllref_clk)) {
637c1c026dbSPhilippe CORNU 		ret = PTR_ERR(dsi->pllref_clk);
638edf20859SYannick Fertre 		dev_err_probe(dev, ret, "Unable to get pll reference clock\n");
639f569aa9bSYannick Fertré 		goto err_clk_get;
640c1c026dbSPhilippe CORNU 	}
641c1c026dbSPhilippe CORNU 
642c1c026dbSPhilippe CORNU 	ret = clk_prepare_enable(dsi->pllref_clk);
643c1c026dbSPhilippe CORNU 	if (ret) {
644f569aa9bSYannick Fertré 		DRM_ERROR("Failed to enable pllref_clk: %d\n", ret);
645f569aa9bSYannick Fertré 		goto err_clk_get;
646c1c026dbSPhilippe CORNU 	}
647c1c026dbSPhilippe CORNU 
648b0e83c2cSYannick Fertre 	dsi->pclk = devm_clk_get(dev, "pclk");
649b0e83c2cSYannick Fertre 	if (IS_ERR(dsi->pclk)) {
650b0e83c2cSYannick Fertre 		ret = PTR_ERR(dsi->pclk);
651fa6251a7SYannick Fertré 		DRM_ERROR("Unable to get peripheral clock: %d\n", ret);
652fa6251a7SYannick Fertré 		goto err_dsi_probe;
653fa6251a7SYannick Fertré 	}
654fa6251a7SYannick Fertré 
655b0e83c2cSYannick Fertre 	ret = clk_prepare_enable(dsi->pclk);
656fa6251a7SYannick Fertré 	if (ret) {
657fa6251a7SYannick Fertré 		DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__);
658fa6251a7SYannick Fertré 		goto err_dsi_probe;
659fa6251a7SYannick Fertré 	}
660fa6251a7SYannick Fertré 
661fa6251a7SYannick Fertré 	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
662b0e83c2cSYannick Fertre 	clk_disable_unprepare(dsi->pclk);
663fa6251a7SYannick Fertré 
664fa6251a7SYannick Fertré 	if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
665fa6251a7SYannick Fertré 		ret = -ENODEV;
666fa6251a7SYannick Fertré 		DRM_ERROR("bad dsi hardware version\n");
667fa6251a7SYannick Fertré 		goto err_dsi_probe;
668fa6251a7SYannick Fertré 	}
669fa6251a7SYannick Fertré 
67022f99f2dSAntonio Borneo 	/* set lane capabilities according to hw version */
67122f99f2dSAntonio Borneo 	dsi->lane_min_kbps = LANE_MIN_KBPS;
67222f99f2dSAntonio Borneo 	dsi->lane_max_kbps = LANE_MAX_KBPS;
67322f99f2dSAntonio Borneo 	if (dsi->hw_version == HWVER_131) {
67422f99f2dSAntonio Borneo 		dsi->lane_min_kbps *= 2;
67522f99f2dSAntonio Borneo 		dsi->lane_max_kbps *= 2;
67622f99f2dSAntonio Borneo 	}
67722f99f2dSAntonio Borneo 
678*185f99b6SRaphael Gallais-Pou 	dsi->pdata = *pdata;
679*185f99b6SRaphael Gallais-Pou 	dsi->pdata.base = dsi->base;
680*185f99b6SRaphael Gallais-Pou 	dsi->pdata.priv_data = dsi;
681*185f99b6SRaphael Gallais-Pou 
682*185f99b6SRaphael Gallais-Pou 	dsi->pdata.max_data_lanes = 2;
683*185f99b6SRaphael Gallais-Pou 	dsi->pdata.phy_ops = &dw_mipi_dsi_stm_phy_ops;
684c1c026dbSPhilippe CORNU 
6858242ecbdSBrian Norris 	platform_set_drvdata(pdev, dsi);
6868242ecbdSBrian Norris 
687*185f99b6SRaphael Gallais-Pou 	dsi->dsi = dw_mipi_dsi_probe(pdev, &dsi->pdata);
6888242ecbdSBrian Norris 	if (IS_ERR(dsi->dsi)) {
689f569aa9bSYannick Fertré 		ret = PTR_ERR(dsi->dsi);
690edf20859SYannick Fertre 		dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n");
691f569aa9bSYannick Fertré 		goto err_dsi_probe;
692c1c026dbSPhilippe CORNU 	}
693c1c026dbSPhilippe CORNU 
694*185f99b6SRaphael Gallais-Pou 	/*
695*185f99b6SRaphael Gallais-Pou 	 * We need to wait for the generic bridge to probe before enabling and
696*185f99b6SRaphael Gallais-Pou 	 * register the internal pixel clock.
697*185f99b6SRaphael Gallais-Pou 	 */
698*185f99b6SRaphael Gallais-Pou 	ret = clk_prepare_enable(dsi->pclk);
699*185f99b6SRaphael Gallais-Pou 	if (ret) {
700*185f99b6SRaphael Gallais-Pou 		DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__);
701*185f99b6SRaphael Gallais-Pou 		goto err_dsi_probe;
702*185f99b6SRaphael Gallais-Pou 	}
703*185f99b6SRaphael Gallais-Pou 
704*185f99b6SRaphael Gallais-Pou 	ret = dw_mipi_dsi_clk_register(dsi, dev);
705*185f99b6SRaphael Gallais-Pou 	if (ret) {
706*185f99b6SRaphael Gallais-Pou 		DRM_ERROR("Failed to register DSI pixel clock: %d\n", ret);
707*185f99b6SRaphael Gallais-Pou 		clk_disable_unprepare(dsi->pclk);
708*185f99b6SRaphael Gallais-Pou 		goto err_dsi_probe;
709*185f99b6SRaphael Gallais-Pou 	}
710*185f99b6SRaphael Gallais-Pou 
711*185f99b6SRaphael Gallais-Pou 	clk_disable_unprepare(dsi->pclk);
712*185f99b6SRaphael Gallais-Pou 
7138242ecbdSBrian Norris 	return 0;
714f569aa9bSYannick Fertré 
715f569aa9bSYannick Fertré err_dsi_probe:
716f569aa9bSYannick Fertré 	clk_disable_unprepare(dsi->pllref_clk);
717f569aa9bSYannick Fertré err_clk_get:
718f569aa9bSYannick Fertré 	regulator_disable(dsi->vdd_supply);
719f569aa9bSYannick Fertré 
720f569aa9bSYannick Fertré 	return ret;
721c1c026dbSPhilippe CORNU }
722c1c026dbSPhilippe CORNU 
dw_mipi_dsi_stm_remove(struct platform_device * pdev)7230c259ab1SUwe Kleine-König static void dw_mipi_dsi_stm_remove(struct platform_device *pdev)
724c1c026dbSPhilippe CORNU {
7258242ecbdSBrian Norris 	struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev);
726c1c026dbSPhilippe CORNU 
7278242ecbdSBrian Norris 	dw_mipi_dsi_remove(dsi->dsi);
728f569aa9bSYannick Fertré 	clk_disable_unprepare(dsi->pllref_clk);
729*185f99b6SRaphael Gallais-Pou 	dw_mipi_dsi_clk_unregister(dsi);
730f569aa9bSYannick Fertré 	regulator_disable(dsi->vdd_supply);
731c1c026dbSPhilippe CORNU }
732c1c026dbSPhilippe CORNU 
dw_mipi_dsi_stm_suspend(struct device * dev)733884d7d03SRaphael Gallais-Pou static int dw_mipi_dsi_stm_suspend(struct device *dev)
7341861a1ffSYannick Fertré {
735*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev);
7361861a1ffSYannick Fertré 
7371861a1ffSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
7381861a1ffSYannick Fertré 
7391861a1ffSYannick Fertré 	clk_disable_unprepare(dsi->pllref_clk);
740b0e83c2cSYannick Fertre 	clk_disable_unprepare(dsi->pclk);
741f569aa9bSYannick Fertré 	regulator_disable(dsi->vdd_supply);
7421861a1ffSYannick Fertré 
7431861a1ffSYannick Fertré 	return 0;
7441861a1ffSYannick Fertré }
7451861a1ffSYannick Fertré 
dw_mipi_dsi_stm_resume(struct device * dev)746884d7d03SRaphael Gallais-Pou static int dw_mipi_dsi_stm_resume(struct device *dev)
7471861a1ffSYannick Fertré {
748*185f99b6SRaphael Gallais-Pou 	struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev);
749f569aa9bSYannick Fertré 	int ret;
7501861a1ffSYannick Fertré 
7511861a1ffSYannick Fertré 	DRM_DEBUG_DRIVER("\n");
7521861a1ffSYannick Fertré 
753f569aa9bSYannick Fertré 	ret = regulator_enable(dsi->vdd_supply);
754f569aa9bSYannick Fertré 	if (ret) {
755f569aa9bSYannick Fertré 		DRM_ERROR("Failed to enable regulator: %d\n", ret);
756f569aa9bSYannick Fertré 		return ret;
757f569aa9bSYannick Fertré 	}
758f569aa9bSYannick Fertré 
759b0e83c2cSYannick Fertre 	ret = clk_prepare_enable(dsi->pclk);
760b0e83c2cSYannick Fertre 	if (ret) {
761b0e83c2cSYannick Fertre 		regulator_disable(dsi->vdd_supply);
762b0e83c2cSYannick Fertre 		DRM_ERROR("Failed to enable pclk: %d\n", ret);
763b0e83c2cSYannick Fertre 		return ret;
764b0e83c2cSYannick Fertre 	}
765b0e83c2cSYannick Fertre 
766f569aa9bSYannick Fertré 	ret = clk_prepare_enable(dsi->pllref_clk);
767f569aa9bSYannick Fertré 	if (ret) {
768b0e83c2cSYannick Fertre 		clk_disable_unprepare(dsi->pclk);
769f569aa9bSYannick Fertré 		regulator_disable(dsi->vdd_supply);
770f569aa9bSYannick Fertré 		DRM_ERROR("Failed to enable pllref_clk: %d\n", ret);
771f569aa9bSYannick Fertré 		return ret;
772f569aa9bSYannick Fertré 	}
7731861a1ffSYannick Fertré 
7741861a1ffSYannick Fertré 	return 0;
7751861a1ffSYannick Fertré }
7761861a1ffSYannick Fertré 
7771861a1ffSYannick Fertré static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = {
778884d7d03SRaphael Gallais-Pou 	SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend,
7791861a1ffSYannick Fertré 			    dw_mipi_dsi_stm_resume)
780b0e83c2cSYannick Fertre 	RUNTIME_PM_OPS(dw_mipi_dsi_stm_suspend,
781b0e83c2cSYannick Fertre 		       dw_mipi_dsi_stm_resume, NULL)
7821861a1ffSYannick Fertré };
7831861a1ffSYannick Fertré 
784c1c026dbSPhilippe CORNU static struct platform_driver dw_mipi_dsi_stm_driver = {
785c1c026dbSPhilippe CORNU 	.probe		= dw_mipi_dsi_stm_probe,
7860c259ab1SUwe Kleine-König 	.remove_new	= dw_mipi_dsi_stm_remove,
787c1c026dbSPhilippe CORNU 	.driver		= {
788c1c026dbSPhilippe CORNU 		.of_match_table = dw_mipi_dsi_stm_dt_ids,
789bb3fb5e5SPhilippe CORNU 		.name	= "stm32-display-dsi",
7901861a1ffSYannick Fertré 		.pm = &dw_mipi_dsi_stm_pm_ops,
791c1c026dbSPhilippe CORNU 	},
792c1c026dbSPhilippe CORNU };
793c1c026dbSPhilippe CORNU 
794c1c026dbSPhilippe CORNU module_platform_driver(dw_mipi_dsi_stm_driver);
795c1c026dbSPhilippe CORNU 
796c1c026dbSPhilippe CORNU MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
797c1c026dbSPhilippe CORNU MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
798c1c026dbSPhilippe CORNU MODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver");
799c1c026dbSPhilippe CORNU MODULE_LICENSE("GPL v2");
800