1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2021 BayLibre, SAS |
4 | * Author: Neil Armstrong <narmstrong@baylibre.com> |
5 | * Copyright (C) 2015 Amlogic, Inc. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of_graph.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/reset.h> |
15 | #include <linux/phy/phy.h> |
16 | #include <linux/bitfield.h> |
17 | |
18 | #include <video/mipi_display.h> |
19 | |
20 | #include <drm/bridge/dw_mipi_dsi.h> |
21 | #include <drm/drm_mipi_dsi.h> |
22 | |
23 | #include <drm/drm_atomic_helper.h> |
24 | #include <drm/drm_device.h> |
25 | #include <drm/drm_probe_helper.h> |
26 | #include <drm/drm_print.h> |
27 | |
28 | #include "meson_drv.h" |
29 | #include "meson_dw_mipi_dsi.h" |
30 | #include "meson_registers.h" |
31 | #include "meson_venc.h" |
32 | |
33 | #define DRIVER_NAME "meson-dw-mipi-dsi" |
34 | #define DRIVER_DESC "Amlogic Meson MIPI-DSI DRM driver" |
35 | |
36 | struct meson_dw_mipi_dsi { |
37 | struct meson_drm *priv; |
38 | struct device *dev; |
39 | void __iomem *base; |
40 | struct phy *phy; |
41 | union phy_configure_opts phy_opts; |
42 | struct dw_mipi_dsi *dmd; |
43 | struct dw_mipi_dsi_plat_data pdata; |
44 | struct mipi_dsi_device *dsi_device; |
45 | const struct drm_display_mode *mode; |
46 | struct clk *bit_clk; |
47 | struct clk *px_clk; |
48 | struct reset_control *top_rst; |
49 | }; |
50 | |
51 | #define encoder_to_meson_dw_mipi_dsi(x) \ |
52 | container_of(x, struct meson_dw_mipi_dsi, encoder) |
53 | |
54 | static void meson_dw_mipi_dsi_hw_init(struct meson_dw_mipi_dsi *mipi_dsi) |
55 | { |
56 | /* Software reset */ |
57 | writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | |
58 | MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, |
59 | MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | |
60 | MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, |
61 | mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); |
62 | writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | |
63 | MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, |
64 | 0, mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); |
65 | |
66 | /* Enable clocks */ |
67 | writel_bits_relaxed(MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, |
68 | MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, |
69 | mipi_dsi->base + MIPI_DSI_TOP_CLK_CNTL); |
70 | |
71 | /* Take memory out of power down */ |
72 | writel_relaxed(0, mipi_dsi->base + MIPI_DSI_TOP_MEM_PD); |
73 | } |
74 | |
75 | static int dw_mipi_dsi_phy_init(void *priv_data) |
76 | { |
77 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
78 | unsigned int dpi_data_format, venc_data_width; |
79 | int ret; |
80 | |
81 | /* Set the bit clock rate to hs_clk_rate */ |
82 | ret = clk_set_rate(clk: mipi_dsi->bit_clk, |
83 | rate: mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate); |
84 | if (ret) { |
85 | dev_err(mipi_dsi->dev, "Failed to set DSI Bit clock rate %lu (ret %d)\n" , |
86 | mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, ret); |
87 | return ret; |
88 | } |
89 | |
90 | /* Make sure the rate of the bit clock is not modified by someone else */ |
91 | ret = clk_rate_exclusive_get(clk: mipi_dsi->bit_clk); |
92 | if (ret) { |
93 | dev_err(mipi_dsi->dev, |
94 | "Failed to set the exclusivity on the bit clock rate (ret %d)\n" , ret); |
95 | return ret; |
96 | } |
97 | |
98 | ret = clk_set_rate(clk: mipi_dsi->px_clk, rate: mipi_dsi->mode->clock * 1000); |
99 | |
100 | if (ret) { |
101 | dev_err(mipi_dsi->dev, "Failed to set DSI Pixel clock rate %u (%d)\n" , |
102 | mipi_dsi->mode->clock * 1000, ret); |
103 | return ret; |
104 | } |
105 | |
106 | switch (mipi_dsi->dsi_device->format) { |
107 | case MIPI_DSI_FMT_RGB888: |
108 | dpi_data_format = DPI_COLOR_24BIT; |
109 | venc_data_width = VENC_IN_COLOR_24B; |
110 | break; |
111 | case MIPI_DSI_FMT_RGB666: |
112 | dpi_data_format = DPI_COLOR_18BIT_CFG_2; |
113 | venc_data_width = VENC_IN_COLOR_18B; |
114 | break; |
115 | case MIPI_DSI_FMT_RGB666_PACKED: |
116 | case MIPI_DSI_FMT_RGB565: |
117 | return -EINVAL; |
118 | } |
119 | |
120 | /* Configure color format for DPI register */ |
121 | writel_relaxed(FIELD_PREP(MIPI_DSI_TOP_DPI_COLOR_MODE, dpi_data_format) | |
122 | FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) | |
123 | FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) | |
124 | FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) | |
125 | FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0), |
126 | mipi_dsi->base + MIPI_DSI_TOP_CNTL); |
127 | |
128 | return phy_configure(phy: mipi_dsi->phy, opts: &mipi_dsi->phy_opts); |
129 | } |
130 | |
131 | static void dw_mipi_dsi_phy_power_on(void *priv_data) |
132 | { |
133 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
134 | |
135 | if (phy_power_on(phy: mipi_dsi->phy)) |
136 | dev_warn(mipi_dsi->dev, "Failed to power on PHY\n" ); |
137 | } |
138 | |
139 | static void dw_mipi_dsi_phy_power_off(void *priv_data) |
140 | { |
141 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
142 | |
143 | if (phy_power_off(phy: mipi_dsi->phy)) |
144 | dev_warn(mipi_dsi->dev, "Failed to power off PHY\n" ); |
145 | |
146 | /* Remove the exclusivity on the bit clock rate */ |
147 | clk_rate_exclusive_put(clk: mipi_dsi->bit_clk); |
148 | } |
149 | |
150 | static int |
151 | dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, |
152 | unsigned long mode_flags, u32 lanes, u32 format, |
153 | unsigned int *lane_mbps) |
154 | { |
155 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
156 | int bpp; |
157 | |
158 | mipi_dsi->mode = mode; |
159 | |
160 | bpp = mipi_dsi_pixel_format_to_bpp(fmt: mipi_dsi->dsi_device->format); |
161 | |
162 | phy_mipi_dphy_get_default_config(pixel_clock: mode->clock * 1000, |
163 | bpp, lanes: mipi_dsi->dsi_device->lanes, |
164 | cfg: &mipi_dsi->phy_opts.mipi_dphy); |
165 | |
166 | *lane_mbps = DIV_ROUND_UP(mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static int |
172 | dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, |
173 | struct dw_mipi_dsi_dphy_timing *timing) |
174 | { |
175 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
176 | |
177 | switch (mipi_dsi->mode->hdisplay) { |
178 | case 240: |
179 | case 768: |
180 | case 1920: |
181 | case 2560: |
182 | timing->clk_lp2hs = 23; |
183 | timing->clk_hs2lp = 38; |
184 | timing->data_lp2hs = 15; |
185 | timing->data_hs2lp = 9; |
186 | break; |
187 | |
188 | default: |
189 | timing->clk_lp2hs = 37; |
190 | timing->clk_hs2lp = 135; |
191 | timing->data_lp2hs = 50; |
192 | timing->data_hs2lp = 3; |
193 | } |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int |
199 | dw_mipi_dsi_get_esc_clk_rate(void *priv_data, unsigned int *esc_clk_rate) |
200 | { |
201 | *esc_clk_rate = 4; /* Mhz */ |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static const struct dw_mipi_dsi_phy_ops meson_dw_mipi_dsi_phy_ops = { |
207 | .init = dw_mipi_dsi_phy_init, |
208 | .power_on = dw_mipi_dsi_phy_power_on, |
209 | .power_off = dw_mipi_dsi_phy_power_off, |
210 | .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, |
211 | .get_timing = dw_mipi_dsi_phy_get_timing, |
212 | .get_esc_clk_rate = dw_mipi_dsi_get_esc_clk_rate, |
213 | }; |
214 | |
215 | static int meson_dw_mipi_dsi_host_attach(void *priv_data, |
216 | struct mipi_dsi_device *device) |
217 | { |
218 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
219 | int ret; |
220 | |
221 | mipi_dsi->dsi_device = device; |
222 | |
223 | switch (device->format) { |
224 | case MIPI_DSI_FMT_RGB888: |
225 | break; |
226 | case MIPI_DSI_FMT_RGB666: |
227 | break; |
228 | case MIPI_DSI_FMT_RGB666_PACKED: |
229 | case MIPI_DSI_FMT_RGB565: |
230 | dev_err(mipi_dsi->dev, "invalid pixel format %d\n" , device->format); |
231 | return -EINVAL; |
232 | } |
233 | |
234 | ret = phy_init(phy: mipi_dsi->phy); |
235 | if (ret) |
236 | return ret; |
237 | |
238 | meson_dw_mipi_dsi_hw_init(mipi_dsi); |
239 | |
240 | return 0; |
241 | } |
242 | |
243 | static int meson_dw_mipi_dsi_host_detach(void *priv_data, |
244 | struct mipi_dsi_device *device) |
245 | { |
246 | struct meson_dw_mipi_dsi *mipi_dsi = priv_data; |
247 | |
248 | if (device == mipi_dsi->dsi_device) |
249 | mipi_dsi->dsi_device = NULL; |
250 | else |
251 | return -EINVAL; |
252 | |
253 | return phy_exit(phy: mipi_dsi->phy); |
254 | } |
255 | |
256 | static const struct dw_mipi_dsi_host_ops meson_dw_mipi_dsi_host_ops = { |
257 | .attach = meson_dw_mipi_dsi_host_attach, |
258 | .detach = meson_dw_mipi_dsi_host_detach, |
259 | }; |
260 | |
261 | static int meson_dw_mipi_dsi_probe(struct platform_device *pdev) |
262 | { |
263 | struct meson_dw_mipi_dsi *mipi_dsi; |
264 | struct device *dev = &pdev->dev; |
265 | |
266 | mipi_dsi = devm_kzalloc(dev, size: sizeof(*mipi_dsi), GFP_KERNEL); |
267 | if (!mipi_dsi) |
268 | return -ENOMEM; |
269 | |
270 | mipi_dsi->base = devm_platform_ioremap_resource(pdev, index: 0); |
271 | if (IS_ERR(ptr: mipi_dsi->base)) |
272 | return PTR_ERR(ptr: mipi_dsi->base); |
273 | |
274 | mipi_dsi->phy = devm_phy_get(dev, string: "dphy" ); |
275 | if (IS_ERR(ptr: mipi_dsi->phy)) |
276 | return dev_err_probe(dev, err: PTR_ERR(ptr: mipi_dsi->phy), |
277 | fmt: "failed to get mipi dphy\n" ); |
278 | |
279 | mipi_dsi->bit_clk = devm_clk_get_enabled(dev, id: "bit" ); |
280 | if (IS_ERR(ptr: mipi_dsi->bit_clk)) { |
281 | int ret = PTR_ERR(ptr: mipi_dsi->bit_clk); |
282 | |
283 | /* TOFIX GP0 on some platforms fails to lock in early boot, defer probe */ |
284 | if (ret == -EIO) |
285 | ret = -EPROBE_DEFER; |
286 | |
287 | return dev_err_probe(dev, err: ret, fmt: "Unable to get enabled bit_clk\n" ); |
288 | } |
289 | |
290 | mipi_dsi->px_clk = devm_clk_get_enabled(dev, id: "px" ); |
291 | if (IS_ERR(ptr: mipi_dsi->px_clk)) |
292 | return dev_err_probe(dev, err: PTR_ERR(ptr: mipi_dsi->px_clk), |
293 | fmt: "Unable to get enabled px_clk\n" ); |
294 | |
295 | /* |
296 | * We use a TOP reset signal because the APB reset signal |
297 | * is handled by the TOP control registers. |
298 | */ |
299 | mipi_dsi->top_rst = devm_reset_control_get_exclusive(dev, id: "top" ); |
300 | if (IS_ERR(ptr: mipi_dsi->top_rst)) |
301 | return dev_err_probe(dev, err: PTR_ERR(ptr: mipi_dsi->top_rst), |
302 | fmt: "Unable to get reset control\n" ); |
303 | |
304 | reset_control_assert(rstc: mipi_dsi->top_rst); |
305 | usleep_range(min: 10, max: 20); |
306 | reset_control_deassert(rstc: mipi_dsi->top_rst); |
307 | |
308 | /* MIPI DSI Controller */ |
309 | |
310 | mipi_dsi->dev = dev; |
311 | mipi_dsi->pdata.base = mipi_dsi->base; |
312 | mipi_dsi->pdata.max_data_lanes = 4; |
313 | mipi_dsi->pdata.phy_ops = &meson_dw_mipi_dsi_phy_ops; |
314 | mipi_dsi->pdata.host_ops = &meson_dw_mipi_dsi_host_ops; |
315 | mipi_dsi->pdata.priv_data = mipi_dsi; |
316 | platform_set_drvdata(pdev, data: mipi_dsi); |
317 | |
318 | mipi_dsi->dmd = dw_mipi_dsi_probe(pdev, plat_data: &mipi_dsi->pdata); |
319 | if (IS_ERR(ptr: mipi_dsi->dmd)) |
320 | return dev_err_probe(dev, err: PTR_ERR(ptr: mipi_dsi->dmd), |
321 | fmt: "Failed to probe dw_mipi_dsi\n" ); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static void meson_dw_mipi_dsi_remove(struct platform_device *pdev) |
327 | { |
328 | struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev); |
329 | |
330 | dw_mipi_dsi_remove(dsi: mipi_dsi->dmd); |
331 | } |
332 | |
333 | static const struct of_device_id meson_dw_mipi_dsi_of_table[] = { |
334 | { .compatible = "amlogic,meson-g12a-dw-mipi-dsi" , }, |
335 | { } |
336 | }; |
337 | MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table); |
338 | |
339 | static struct platform_driver meson_dw_mipi_dsi_platform_driver = { |
340 | .probe = meson_dw_mipi_dsi_probe, |
341 | .remove_new = meson_dw_mipi_dsi_remove, |
342 | .driver = { |
343 | .name = DRIVER_NAME, |
344 | .of_match_table = meson_dw_mipi_dsi_of_table, |
345 | }, |
346 | }; |
347 | module_platform_driver(meson_dw_mipi_dsi_platform_driver); |
348 | |
349 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>" ); |
350 | MODULE_DESCRIPTION(DRIVER_DESC); |
351 | MODULE_LICENSE("GPL" ); |
352 | |