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