1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2017 |
4 | * |
5 | * Authors: Philippe Cornu <philippe.cornu@st.com> |
6 | * Yannick Fertre <yannick.fertre@st.com> |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/iopoll.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/regulator/consumer.h> |
15 | |
16 | #include <video/mipi_display.h> |
17 | |
18 | #include <drm/bridge/dw_mipi_dsi.h> |
19 | #include <drm/drm_mipi_dsi.h> |
20 | #include <drm/drm_print.h> |
21 | |
22 | #define HWVER_130 0x31333000 /* IP version 1.30 */ |
23 | #define HWVER_131 0x31333100 /* IP version 1.31 */ |
24 | |
25 | /* DSI digital registers & bit definitions */ |
26 | #define DSI_VERSION 0x00 |
27 | #define VERSION GENMASK(31, 8) |
28 | |
29 | /* DSI wrapper registers & bit definitions */ |
30 | /* Note: registers are named as in the Reference Manual */ |
31 | #define DSI_WCFGR 0x0400 /* Wrapper ConFiGuration Reg */ |
32 | #define WCFGR_DSIM BIT(0) /* DSI Mode */ |
33 | #define WCFGR_COLMUX GENMASK(3, 1) /* COLor MUltipleXing */ |
34 | |
35 | #define DSI_WCR 0x0404 /* Wrapper Control Reg */ |
36 | #define WCR_DSIEN BIT(3) /* DSI ENable */ |
37 | |
38 | #define DSI_WISR 0x040C /* Wrapper Interrupt and Status Reg */ |
39 | #define WISR_PLLLS BIT(8) /* PLL Lock Status */ |
40 | #define WISR_RRS BIT(12) /* Regulator Ready Status */ |
41 | |
42 | #define DSI_WPCR0 0x0418 /* Wrapper Phy Conf Reg 0 */ |
43 | #define WPCR0_UIX4 GENMASK(5, 0) /* Unit Interval X 4 */ |
44 | #define WPCR0_TDDL BIT(16) /* Turn Disable Data Lanes */ |
45 | |
46 | #define DSI_WRPCR 0x0430 /* Wrapper Regulator & Pll Ctrl Reg */ |
47 | #define WRPCR_PLLEN BIT(0) /* PLL ENable */ |
48 | #define WRPCR_NDIV GENMASK(8, 2) /* pll loop DIVision Factor */ |
49 | #define WRPCR_IDF GENMASK(14, 11) /* pll Input Division Factor */ |
50 | #define WRPCR_ODF GENMASK(17, 16) /* pll Output Division Factor */ |
51 | #define WRPCR_REGEN BIT(24) /* REGulator ENable */ |
52 | #define WRPCR_BGREN BIT(28) /* BandGap Reference ENable */ |
53 | #define IDF_MIN 1 |
54 | #define IDF_MAX 7 |
55 | #define NDIV_MIN 10 |
56 | #define NDIV_MAX 125 |
57 | #define ODF_MIN 1 |
58 | #define ODF_MAX 8 |
59 | |
60 | /* dsi color format coding according to the datasheet */ |
61 | enum dsi_color { |
62 | DSI_RGB565_CONF1, |
63 | DSI_RGB565_CONF2, |
64 | DSI_RGB565_CONF3, |
65 | DSI_RGB666_CONF1, |
66 | DSI_RGB666_CONF2, |
67 | DSI_RGB888, |
68 | }; |
69 | |
70 | #define LANE_MIN_KBPS 31250 |
71 | #define LANE_MAX_KBPS 500000 |
72 | |
73 | /* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */ |
74 | #define SLEEP_US 1000 |
75 | #define TIMEOUT_US 200000 |
76 | |
77 | struct dw_mipi_dsi_stm { |
78 | void __iomem *base; |
79 | struct clk *pllref_clk; |
80 | struct dw_mipi_dsi *dsi; |
81 | u32 hw_version; |
82 | int lane_min_kbps; |
83 | int lane_max_kbps; |
84 | struct regulator *vdd_supply; |
85 | }; |
86 | |
87 | static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val) |
88 | { |
89 | writel(val, addr: dsi->base + reg); |
90 | } |
91 | |
92 | static inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg) |
93 | { |
94 | return readl(addr: dsi->base + reg); |
95 | } |
96 | |
97 | static inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) |
98 | { |
99 | dsi_write(dsi, reg, val: dsi_read(dsi, reg) | mask); |
100 | } |
101 | |
102 | static inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) |
103 | { |
104 | dsi_write(dsi, reg, val: dsi_read(dsi, reg) & ~mask); |
105 | } |
106 | |
107 | static inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg, |
108 | u32 mask, u32 val) |
109 | { |
110 | dsi_write(dsi, reg, val: (dsi_read(dsi, reg) & ~mask) | val); |
111 | } |
112 | |
113 | static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt) |
114 | { |
115 | switch (fmt) { |
116 | case MIPI_DSI_FMT_RGB888: |
117 | return DSI_RGB888; |
118 | case MIPI_DSI_FMT_RGB666: |
119 | return DSI_RGB666_CONF2; |
120 | case MIPI_DSI_FMT_RGB666_PACKED: |
121 | return DSI_RGB666_CONF1; |
122 | case MIPI_DSI_FMT_RGB565: |
123 | return DSI_RGB565_CONF1; |
124 | default: |
125 | DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n" ); |
126 | } |
127 | return DSI_RGB888; |
128 | } |
129 | |
130 | static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf) |
131 | { |
132 | int divisor = idf * odf; |
133 | |
134 | /* prevent from division by 0 */ |
135 | if (!divisor) |
136 | return 0; |
137 | |
138 | return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor); |
139 | } |
140 | |
141 | static int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi, |
142 | int clkin_khz, int clkout_khz, |
143 | int *idf, int *ndiv, int *odf) |
144 | { |
145 | int i, o, n, n_min, n_max; |
146 | int fvco_min, fvco_max, delta, best_delta; /* all in khz */ |
147 | |
148 | /* Early checks preventing division by 0 & odd results */ |
149 | if (clkin_khz <= 0 || clkout_khz <= 0) |
150 | return -EINVAL; |
151 | |
152 | fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX; |
153 | fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN; |
154 | |
155 | best_delta = 1000000; /* big started value (1000000khz) */ |
156 | |
157 | for (i = IDF_MIN; i <= IDF_MAX; i++) { |
158 | /* Compute ndiv range according to Fvco */ |
159 | n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1; |
160 | n_max = (fvco_max * i) / (2 * clkin_khz); |
161 | |
162 | /* No need to continue idf loop if we reach ndiv max */ |
163 | if (n_min >= NDIV_MAX) |
164 | break; |
165 | |
166 | /* Clamp ndiv to valid values */ |
167 | if (n_min < NDIV_MIN) |
168 | n_min = NDIV_MIN; |
169 | if (n_max > NDIV_MAX) |
170 | n_max = NDIV_MAX; |
171 | |
172 | for (o = ODF_MIN; o <= ODF_MAX; o *= 2) { |
173 | n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz); |
174 | /* Check ndiv according to vco range */ |
175 | if (n < n_min || n > n_max) |
176 | continue; |
177 | /* Check if new delta is better & saves parameters */ |
178 | delta = dsi_pll_get_clkout_khz(clkin_khz, idf: i, ndiv: n, odf: o) - |
179 | clkout_khz; |
180 | if (delta < 0) |
181 | delta = -delta; |
182 | if (delta < best_delta) { |
183 | *idf = i; |
184 | *ndiv = n; |
185 | *odf = o; |
186 | best_delta = delta; |
187 | } |
188 | /* fast return in case of "perfect result" */ |
189 | if (!delta) |
190 | return 0; |
191 | } |
192 | } |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static int dw_mipi_dsi_phy_init(void *priv_data) |
198 | { |
199 | struct dw_mipi_dsi_stm *dsi = priv_data; |
200 | u32 val; |
201 | int ret; |
202 | |
203 | /* Enable the regulator */ |
204 | dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); |
205 | ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, |
206 | SLEEP_US, TIMEOUT_US); |
207 | if (ret) |
208 | DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n" ); |
209 | |
210 | /* Enable the DSI PLL & wait for its lock */ |
211 | dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN); |
212 | ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, |
213 | SLEEP_US, TIMEOUT_US); |
214 | if (ret) |
215 | DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n" ); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static void dw_mipi_dsi_phy_power_on(void *priv_data) |
221 | { |
222 | struct dw_mipi_dsi_stm *dsi = priv_data; |
223 | |
224 | DRM_DEBUG_DRIVER("\n" ); |
225 | |
226 | /* Enable the DSI wrapper */ |
227 | dsi_set(dsi, DSI_WCR, WCR_DSIEN); |
228 | } |
229 | |
230 | static void dw_mipi_dsi_phy_power_off(void *priv_data) |
231 | { |
232 | struct dw_mipi_dsi_stm *dsi = priv_data; |
233 | |
234 | DRM_DEBUG_DRIVER("\n" ); |
235 | |
236 | /* Disable the DSI wrapper */ |
237 | dsi_clear(dsi, DSI_WCR, WCR_DSIEN); |
238 | } |
239 | |
240 | static int |
241 | dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, |
242 | unsigned long mode_flags, u32 lanes, u32 format, |
243 | unsigned int *lane_mbps) |
244 | { |
245 | struct dw_mipi_dsi_stm *dsi = priv_data; |
246 | unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; |
247 | int ret, bpp; |
248 | u32 val; |
249 | |
250 | pll_in_khz = (unsigned int)(clk_get_rate(clk: dsi->pllref_clk) / 1000); |
251 | |
252 | /* Compute requested pll out */ |
253 | bpp = mipi_dsi_pixel_format_to_bpp(fmt: format); |
254 | pll_out_khz = mode->clock * bpp / lanes; |
255 | |
256 | /* Add 20% to pll out to be higher than pixel bw (burst mode only) */ |
257 | if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) |
258 | pll_out_khz = (pll_out_khz * 12) / 10; |
259 | |
260 | if (pll_out_khz > dsi->lane_max_kbps) { |
261 | pll_out_khz = dsi->lane_max_kbps; |
262 | DRM_WARN("Warning max phy mbps is used\n" ); |
263 | } |
264 | if (pll_out_khz < dsi->lane_min_kbps) { |
265 | pll_out_khz = dsi->lane_min_kbps; |
266 | DRM_WARN("Warning min phy mbps is used\n" ); |
267 | } |
268 | |
269 | /* Compute best pll parameters */ |
270 | idf = 0; |
271 | ndiv = 0; |
272 | odf = 0; |
273 | ret = dsi_pll_get_params(dsi, clkin_khz: pll_in_khz, clkout_khz: pll_out_khz, |
274 | idf: &idf, ndiv: &ndiv, odf: &odf); |
275 | if (ret) |
276 | DRM_WARN("Warning dsi_pll_get_params(): bad params\n" ); |
277 | |
278 | /* Get the adjusted pll out value */ |
279 | pll_out_khz = dsi_pll_get_clkout_khz(clkin_khz: pll_in_khz, idf, ndiv, odf); |
280 | |
281 | /* Set the PLL division factors */ |
282 | dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, |
283 | val: (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); |
284 | |
285 | /* Compute uix4 & set the bit period in high-speed mode */ |
286 | val = 4000000 / pll_out_khz; |
287 | dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); |
288 | |
289 | /* Select video mode by resetting DSIM bit */ |
290 | dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM); |
291 | |
292 | /* Select the color coding */ |
293 | dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX, |
294 | val: dsi_color_from_mipi(fmt: format) << 1); |
295 | |
296 | *lane_mbps = pll_out_khz / 1000; |
297 | |
298 | DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n" , |
299 | pll_in_khz, pll_out_khz, *lane_mbps); |
300 | |
301 | return 0; |
302 | } |
303 | |
304 | #define DSI_PHY_DELAY(fp, vp, mbps) DIV_ROUND_UP((fp) * (mbps) + 1000 * (vp), 8000) |
305 | |
306 | static int |
307 | dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, |
308 | struct dw_mipi_dsi_dphy_timing *timing) |
309 | { |
310 | /* |
311 | * From STM32MP157 datasheet, valid for STM32F469, STM32F7x9, STM32H747 |
312 | * phy_clkhs2lp_time = (272+136*UI)/(8*UI) |
313 | * phy_clklp2hs_time = (512+40*UI)/(8*UI) |
314 | * phy_hs2lp_time = (192+64*UI)/(8*UI) |
315 | * phy_lp2hs_time = (256+32*UI)/(8*UI) |
316 | */ |
317 | timing->clk_hs2lp = DSI_PHY_DELAY(272, 136, lane_mbps); |
318 | timing->clk_lp2hs = DSI_PHY_DELAY(512, 40, lane_mbps); |
319 | timing->data_hs2lp = DSI_PHY_DELAY(192, 64, lane_mbps); |
320 | timing->data_lp2hs = DSI_PHY_DELAY(256, 32, lane_mbps); |
321 | |
322 | return 0; |
323 | } |
324 | |
325 | #define CLK_TOLERANCE_HZ 50 |
326 | |
327 | static enum drm_mode_status |
328 | dw_mipi_dsi_stm_mode_valid(void *priv_data, |
329 | const struct drm_display_mode *mode, |
330 | unsigned long mode_flags, u32 lanes, u32 format) |
331 | { |
332 | struct dw_mipi_dsi_stm *dsi = priv_data; |
333 | unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; |
334 | int ret, bpp; |
335 | |
336 | bpp = mipi_dsi_pixel_format_to_bpp(fmt: format); |
337 | if (bpp < 0) |
338 | return MODE_BAD; |
339 | |
340 | /* Compute requested pll out */ |
341 | pll_out_khz = mode->clock * bpp / lanes; |
342 | |
343 | if (pll_out_khz > dsi->lane_max_kbps) |
344 | return MODE_CLOCK_HIGH; |
345 | |
346 | if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
347 | /* Add 20% to pll out to be higher than pixel bw */ |
348 | pll_out_khz = (pll_out_khz * 12) / 10; |
349 | } else { |
350 | if (pll_out_khz < dsi->lane_min_kbps) |
351 | return MODE_CLOCK_LOW; |
352 | } |
353 | |
354 | /* Compute best pll parameters */ |
355 | idf = 0; |
356 | ndiv = 0; |
357 | odf = 0; |
358 | pll_in_khz = clk_get_rate(clk: dsi->pllref_clk) / 1000; |
359 | ret = dsi_pll_get_params(dsi, clkin_khz: pll_in_khz, clkout_khz: pll_out_khz, idf: &idf, ndiv: &ndiv, odf: &odf); |
360 | if (ret) { |
361 | DRM_WARN("Warning dsi_pll_get_params(): bad params\n" ); |
362 | return MODE_ERROR; |
363 | } |
364 | |
365 | if (!(mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) { |
366 | unsigned int px_clock_hz, target_px_clock_hz, lane_mbps; |
367 | int dsi_short_packet_size_px, hfp, hsync, hbp, delay_to_lp; |
368 | struct dw_mipi_dsi_dphy_timing dphy_timing; |
369 | |
370 | /* Get the adjusted pll out value */ |
371 | pll_out_khz = dsi_pll_get_clkout_khz(clkin_khz: pll_in_khz, idf, ndiv, odf); |
372 | |
373 | px_clock_hz = DIV_ROUND_CLOSEST_ULL(1000ULL * pll_out_khz * lanes, bpp); |
374 | target_px_clock_hz = mode->clock * 1000; |
375 | /* |
376 | * Filter modes according to the clock value, particularly useful for |
377 | * hdmi modes that require precise pixel clocks. |
378 | */ |
379 | if (px_clock_hz < target_px_clock_hz - CLK_TOLERANCE_HZ || |
380 | px_clock_hz > target_px_clock_hz + CLK_TOLERANCE_HZ) |
381 | return MODE_CLOCK_RANGE; |
382 | |
383 | /* sync packets are codes as DSI short packets (4 bytes) */ |
384 | dsi_short_packet_size_px = DIV_ROUND_UP(4 * BITS_PER_BYTE, bpp); |
385 | |
386 | hfp = mode->hsync_start - mode->hdisplay; |
387 | hsync = mode->hsync_end - mode->hsync_start; |
388 | hbp = mode->htotal - mode->hsync_end; |
389 | |
390 | /* hsync must be longer than 4 bytes HSS packets */ |
391 | if (hsync < dsi_short_packet_size_px) |
392 | return MODE_HSYNC_NARROW; |
393 | |
394 | if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { |
395 | /* HBP must be longer than 4 bytes HSE packets */ |
396 | if (hbp < dsi_short_packet_size_px) |
397 | return MODE_HSYNC_NARROW; |
398 | hbp -= dsi_short_packet_size_px; |
399 | } else { |
400 | /* With sync events HBP extends in the hsync */ |
401 | hbp += hsync - dsi_short_packet_size_px; |
402 | } |
403 | |
404 | lane_mbps = pll_out_khz / 1000; |
405 | ret = dw_mipi_dsi_phy_get_timing(priv_data, lane_mbps, timing: &dphy_timing); |
406 | if (ret) |
407 | return MODE_ERROR; |
408 | /* |
409 | * In non-burst mode DSI has to enter in LP during HFP |
410 | * (horizontal front porch) or HBP (horizontal back porch) to |
411 | * resync with LTDC pixel clock. |
412 | */ |
413 | delay_to_lp = DIV_ROUND_UP((dphy_timing.data_hs2lp + dphy_timing.data_lp2hs) * |
414 | lanes * BITS_PER_BYTE, bpp); |
415 | if (hfp < delay_to_lp && hbp < delay_to_lp) |
416 | return MODE_HSYNC; |
417 | } |
418 | |
419 | return MODE_OK; |
420 | } |
421 | |
422 | static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { |
423 | .init = dw_mipi_dsi_phy_init, |
424 | .power_on = dw_mipi_dsi_phy_power_on, |
425 | .power_off = dw_mipi_dsi_phy_power_off, |
426 | .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, |
427 | .get_timing = dw_mipi_dsi_phy_get_timing, |
428 | }; |
429 | |
430 | static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = { |
431 | .max_data_lanes = 2, |
432 | .mode_valid = dw_mipi_dsi_stm_mode_valid, |
433 | .phy_ops = &dw_mipi_dsi_stm_phy_ops, |
434 | }; |
435 | |
436 | static const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = { |
437 | { .compatible = "st,stm32-dsi" , .data = &dw_mipi_dsi_stm_plat_data, }, |
438 | { }, |
439 | }; |
440 | MODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids); |
441 | |
442 | static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) |
443 | { |
444 | struct device *dev = &pdev->dev; |
445 | struct dw_mipi_dsi_stm *dsi; |
446 | struct clk *pclk; |
447 | int ret; |
448 | |
449 | dsi = devm_kzalloc(dev, size: sizeof(*dsi), GFP_KERNEL); |
450 | if (!dsi) |
451 | return -ENOMEM; |
452 | |
453 | dsi->base = devm_platform_ioremap_resource(pdev, index: 0); |
454 | if (IS_ERR(ptr: dsi->base)) { |
455 | ret = PTR_ERR(ptr: dsi->base); |
456 | DRM_ERROR("Unable to get dsi registers %d\n" , ret); |
457 | return ret; |
458 | } |
459 | |
460 | dsi->vdd_supply = devm_regulator_get(dev, id: "phy-dsi" ); |
461 | if (IS_ERR(ptr: dsi->vdd_supply)) { |
462 | ret = PTR_ERR(ptr: dsi->vdd_supply); |
463 | dev_err_probe(dev, err: ret, fmt: "Failed to request regulator\n" ); |
464 | return ret; |
465 | } |
466 | |
467 | ret = regulator_enable(regulator: dsi->vdd_supply); |
468 | if (ret) { |
469 | DRM_ERROR("Failed to enable regulator: %d\n" , ret); |
470 | return ret; |
471 | } |
472 | |
473 | dsi->pllref_clk = devm_clk_get(dev, id: "ref" ); |
474 | if (IS_ERR(ptr: dsi->pllref_clk)) { |
475 | ret = PTR_ERR(ptr: dsi->pllref_clk); |
476 | dev_err_probe(dev, err: ret, fmt: "Unable to get pll reference clock\n" ); |
477 | goto err_clk_get; |
478 | } |
479 | |
480 | ret = clk_prepare_enable(clk: dsi->pllref_clk); |
481 | if (ret) { |
482 | DRM_ERROR("Failed to enable pllref_clk: %d\n" , ret); |
483 | goto err_clk_get; |
484 | } |
485 | |
486 | pclk = devm_clk_get(dev, id: "pclk" ); |
487 | if (IS_ERR(ptr: pclk)) { |
488 | ret = PTR_ERR(ptr: pclk); |
489 | DRM_ERROR("Unable to get peripheral clock: %d\n" , ret); |
490 | goto err_dsi_probe; |
491 | } |
492 | |
493 | ret = clk_prepare_enable(clk: pclk); |
494 | if (ret) { |
495 | DRM_ERROR("%s: Failed to enable peripheral clk\n" , __func__); |
496 | goto err_dsi_probe; |
497 | } |
498 | |
499 | dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; |
500 | clk_disable_unprepare(clk: pclk); |
501 | |
502 | if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { |
503 | ret = -ENODEV; |
504 | DRM_ERROR("bad dsi hardware version\n" ); |
505 | goto err_dsi_probe; |
506 | } |
507 | |
508 | /* set lane capabilities according to hw version */ |
509 | dsi->lane_min_kbps = LANE_MIN_KBPS; |
510 | dsi->lane_max_kbps = LANE_MAX_KBPS; |
511 | if (dsi->hw_version == HWVER_131) { |
512 | dsi->lane_min_kbps *= 2; |
513 | dsi->lane_max_kbps *= 2; |
514 | } |
515 | |
516 | dw_mipi_dsi_stm_plat_data.base = dsi->base; |
517 | dw_mipi_dsi_stm_plat_data.priv_data = dsi; |
518 | |
519 | platform_set_drvdata(pdev, data: dsi); |
520 | |
521 | dsi->dsi = dw_mipi_dsi_probe(pdev, plat_data: &dw_mipi_dsi_stm_plat_data); |
522 | if (IS_ERR(ptr: dsi->dsi)) { |
523 | ret = PTR_ERR(ptr: dsi->dsi); |
524 | dev_err_probe(dev, err: ret, fmt: "Failed to initialize mipi dsi host\n" ); |
525 | goto err_dsi_probe; |
526 | } |
527 | |
528 | return 0; |
529 | |
530 | err_dsi_probe: |
531 | clk_disable_unprepare(clk: dsi->pllref_clk); |
532 | err_clk_get: |
533 | regulator_disable(regulator: dsi->vdd_supply); |
534 | |
535 | return ret; |
536 | } |
537 | |
538 | static void dw_mipi_dsi_stm_remove(struct platform_device *pdev) |
539 | { |
540 | struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev); |
541 | |
542 | dw_mipi_dsi_remove(dsi: dsi->dsi); |
543 | clk_disable_unprepare(clk: dsi->pllref_clk); |
544 | regulator_disable(regulator: dsi->vdd_supply); |
545 | } |
546 | |
547 | static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) |
548 | { |
549 | struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; |
550 | |
551 | DRM_DEBUG_DRIVER("\n" ); |
552 | |
553 | clk_disable_unprepare(clk: dsi->pllref_clk); |
554 | regulator_disable(regulator: dsi->vdd_supply); |
555 | |
556 | return 0; |
557 | } |
558 | |
559 | static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) |
560 | { |
561 | struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; |
562 | int ret; |
563 | |
564 | DRM_DEBUG_DRIVER("\n" ); |
565 | |
566 | ret = regulator_enable(regulator: dsi->vdd_supply); |
567 | if (ret) { |
568 | DRM_ERROR("Failed to enable regulator: %d\n" , ret); |
569 | return ret; |
570 | } |
571 | |
572 | ret = clk_prepare_enable(clk: dsi->pllref_clk); |
573 | if (ret) { |
574 | regulator_disable(regulator: dsi->vdd_supply); |
575 | DRM_ERROR("Failed to enable pllref_clk: %d\n" , ret); |
576 | return ret; |
577 | } |
578 | |
579 | return 0; |
580 | } |
581 | |
582 | static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = { |
583 | SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend, |
584 | dw_mipi_dsi_stm_resume) |
585 | }; |
586 | |
587 | static struct platform_driver dw_mipi_dsi_stm_driver = { |
588 | .probe = dw_mipi_dsi_stm_probe, |
589 | .remove_new = dw_mipi_dsi_stm_remove, |
590 | .driver = { |
591 | .of_match_table = dw_mipi_dsi_stm_dt_ids, |
592 | .name = "stm32-display-dsi" , |
593 | .pm = &dw_mipi_dsi_stm_pm_ops, |
594 | }, |
595 | }; |
596 | |
597 | module_platform_driver(dw_mipi_dsi_stm_driver); |
598 | |
599 | MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>" ); |
600 | MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>" ); |
601 | MODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver" ); |
602 | MODULE_LICENSE("GPL v2" ); |
603 | |