1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ARC PGU DRM driver. |
4 | * |
5 | * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <drm/drm_atomic_helper.h> |
10 | #include <drm/drm_debugfs.h> |
11 | #include <drm/drm_device.h> |
12 | #include <drm/drm_drv.h> |
13 | #include <drm/drm_edid.h> |
14 | #include <drm/drm_fb_dma_helper.h> |
15 | #include <drm/drm_fbdev_dma.h> |
16 | #include <drm/drm_fourcc.h> |
17 | #include <drm/drm_framebuffer.h> |
18 | #include <drm/drm_gem_dma_helper.h> |
19 | #include <drm/drm_gem_framebuffer_helper.h> |
20 | #include <drm/drm_module.h> |
21 | #include <drm/drm_of.h> |
22 | #include <drm/drm_probe_helper.h> |
23 | #include <drm/drm_simple_kms_helper.h> |
24 | #include <linux/dma-mapping.h> |
25 | #include <linux/module.h> |
26 | #include <linux/of_reserved_mem.h> |
27 | #include <linux/platform_device.h> |
28 | |
29 | #define ARCPGU_REG_CTRL 0x00 |
30 | #define ARCPGU_REG_STAT 0x04 |
31 | #define ARCPGU_REG_FMT 0x10 |
32 | #define ARCPGU_REG_HSYNC 0x14 |
33 | #define ARCPGU_REG_VSYNC 0x18 |
34 | #define ARCPGU_REG_ACTIVE 0x1c |
35 | #define ARCPGU_REG_BUF0_ADDR 0x40 |
36 | #define ARCPGU_REG_STRIDE 0x50 |
37 | #define ARCPGU_REG_START_SET 0x84 |
38 | |
39 | #define ARCPGU_REG_ID 0x3FC |
40 | |
41 | #define ARCPGU_CTRL_ENABLE_MASK 0x02 |
42 | #define ARCPGU_CTRL_VS_POL_MASK 0x1 |
43 | #define ARCPGU_CTRL_VS_POL_OFST 0x3 |
44 | #define ARCPGU_CTRL_HS_POL_MASK 0x1 |
45 | #define ARCPGU_CTRL_HS_POL_OFST 0x4 |
46 | #define ARCPGU_MODE_XRGB8888 BIT(2) |
47 | #define ARCPGU_STAT_BUSY_MASK 0x02 |
48 | |
49 | struct arcpgu_drm_private { |
50 | struct drm_device drm; |
51 | void __iomem *regs; |
52 | struct clk *clk; |
53 | struct drm_simple_display_pipe pipe; |
54 | struct drm_connector sim_conn; |
55 | }; |
56 | |
57 | #define dev_to_arcpgu(x) container_of(x, struct arcpgu_drm_private, drm) |
58 | |
59 | #define pipe_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, pipe) |
60 | |
61 | static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu, |
62 | unsigned int reg, u32 value) |
63 | { |
64 | iowrite32(value, arcpgu->regs + reg); |
65 | } |
66 | |
67 | static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu, |
68 | unsigned int reg) |
69 | { |
70 | return ioread32(arcpgu->regs + reg); |
71 | } |
72 | |
73 | #define XRES_DEF 640 |
74 | #define YRES_DEF 480 |
75 | |
76 | #define XRES_MAX 8192 |
77 | #define YRES_MAX 8192 |
78 | |
79 | static int arcpgu_drm_connector_get_modes(struct drm_connector *connector) |
80 | { |
81 | int count; |
82 | |
83 | count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); |
84 | drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF); |
85 | return count; |
86 | } |
87 | |
88 | static const struct drm_connector_helper_funcs |
89 | arcpgu_drm_connector_helper_funcs = { |
90 | .get_modes = arcpgu_drm_connector_get_modes, |
91 | }; |
92 | |
93 | static const struct drm_connector_funcs arcpgu_drm_connector_funcs = { |
94 | .reset = drm_atomic_helper_connector_reset, |
95 | .fill_modes = drm_helper_probe_single_connector_modes, |
96 | .destroy = drm_connector_cleanup, |
97 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
98 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
99 | }; |
100 | |
101 | static int arcpgu_drm_sim_init(struct drm_device *drm, struct drm_connector *connector) |
102 | { |
103 | drm_connector_helper_add(connector, funcs: &arcpgu_drm_connector_helper_funcs); |
104 | return drm_connector_init(dev: drm, connector, funcs: &arcpgu_drm_connector_funcs, |
105 | DRM_MODE_CONNECTOR_VIRTUAL); |
106 | } |
107 | |
108 | #define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1)) |
109 | |
110 | static const u32 arc_pgu_supported_formats[] = { |
111 | DRM_FORMAT_RGB565, |
112 | DRM_FORMAT_XRGB8888, |
113 | DRM_FORMAT_ARGB8888, |
114 | }; |
115 | |
116 | static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu) |
117 | { |
118 | const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb; |
119 | uint32_t pixel_format = fb->format->format; |
120 | u32 format = DRM_FORMAT_INVALID; |
121 | int i; |
122 | u32 reg_ctrl; |
123 | |
124 | for (i = 0; i < ARRAY_SIZE(arc_pgu_supported_formats); i++) { |
125 | if (arc_pgu_supported_formats[i] == pixel_format) |
126 | format = arc_pgu_supported_formats[i]; |
127 | } |
128 | |
129 | if (WARN_ON(format == DRM_FORMAT_INVALID)) |
130 | return; |
131 | |
132 | reg_ctrl = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); |
133 | if (format == DRM_FORMAT_RGB565) |
134 | reg_ctrl &= ~ARCPGU_MODE_XRGB8888; |
135 | else |
136 | reg_ctrl |= ARCPGU_MODE_XRGB8888; |
137 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, value: reg_ctrl); |
138 | } |
139 | |
140 | static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *pipe, |
141 | const struct drm_display_mode *mode) |
142 | { |
143 | struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe); |
144 | long rate, clk_rate = mode->clock * 1000; |
145 | long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */ |
146 | |
147 | rate = clk_round_rate(clk: arcpgu->clk, rate: clk_rate); |
148 | if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0)) |
149 | return MODE_OK; |
150 | |
151 | return MODE_NOCLOCK; |
152 | } |
153 | |
154 | static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu) |
155 | { |
156 | struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode; |
157 | u32 val; |
158 | |
159 | arc_pgu_write(arcpgu, ARCPGU_REG_FMT, |
160 | ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal)); |
161 | |
162 | arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC, |
163 | ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay, |
164 | m->crtc_hsync_end - m->crtc_hdisplay)); |
165 | |
166 | arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC, |
167 | ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay, |
168 | m->crtc_vsync_end - m->crtc_vdisplay)); |
169 | |
170 | arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE, |
171 | ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start, |
172 | m->crtc_vblank_end - m->crtc_vblank_start)); |
173 | |
174 | val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); |
175 | |
176 | if (m->flags & DRM_MODE_FLAG_PVSYNC) |
177 | val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST; |
178 | else |
179 | val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST); |
180 | |
181 | if (m->flags & DRM_MODE_FLAG_PHSYNC) |
182 | val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST; |
183 | else |
184 | val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST); |
185 | |
186 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, value: val); |
187 | arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, value: 0); |
188 | arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, value: 1); |
189 | |
190 | arc_pgu_set_pxl_fmt(arcpgu); |
191 | |
192 | clk_set_rate(clk: arcpgu->clk, rate: m->crtc_clock * 1000); |
193 | } |
194 | |
195 | static void arc_pgu_enable(struct drm_simple_display_pipe *pipe, |
196 | struct drm_crtc_state *crtc_state, |
197 | struct drm_plane_state *plane_state) |
198 | { |
199 | struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe); |
200 | |
201 | arc_pgu_mode_set(arcpgu); |
202 | |
203 | clk_prepare_enable(clk: arcpgu->clk); |
204 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, |
205 | value: arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | |
206 | ARCPGU_CTRL_ENABLE_MASK); |
207 | } |
208 | |
209 | static void arc_pgu_disable(struct drm_simple_display_pipe *pipe) |
210 | { |
211 | struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe); |
212 | |
213 | clk_disable_unprepare(clk: arcpgu->clk); |
214 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, |
215 | value: arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) & |
216 | ~ARCPGU_CTRL_ENABLE_MASK); |
217 | } |
218 | |
219 | static void arc_pgu_update(struct drm_simple_display_pipe *pipe, |
220 | struct drm_plane_state *state) |
221 | { |
222 | struct arcpgu_drm_private *arcpgu; |
223 | struct drm_gem_dma_object *gem; |
224 | |
225 | if (!pipe->plane.state->fb) |
226 | return; |
227 | |
228 | arcpgu = pipe_to_arcpgu_priv(pipe); |
229 | gem = drm_fb_dma_get_gem_obj(fb: pipe->plane.state->fb, plane: 0); |
230 | arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, value: gem->dma_addr); |
231 | } |
232 | |
233 | static const struct drm_simple_display_pipe_funcs arc_pgu_pipe_funcs = { |
234 | .update = arc_pgu_update, |
235 | .mode_valid = arc_pgu_mode_valid, |
236 | .enable = arc_pgu_enable, |
237 | .disable = arc_pgu_disable, |
238 | }; |
239 | |
240 | static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = { |
241 | .fb_create = drm_gem_fb_create, |
242 | .atomic_check = drm_atomic_helper_check, |
243 | .atomic_commit = drm_atomic_helper_commit, |
244 | }; |
245 | |
246 | DEFINE_DRM_GEM_DMA_FOPS(arcpgu_drm_ops); |
247 | |
248 | static int arcpgu_load(struct arcpgu_drm_private *arcpgu) |
249 | { |
250 | struct platform_device *pdev = to_platform_device(arcpgu->drm.dev); |
251 | struct device_node *encoder_node = NULL, *endpoint_node = NULL; |
252 | struct drm_connector *connector = NULL; |
253 | struct drm_device *drm = &arcpgu->drm; |
254 | struct resource *res; |
255 | int ret; |
256 | |
257 | arcpgu->clk = devm_clk_get(dev: drm->dev, id: "pxlclk" ); |
258 | if (IS_ERR(ptr: arcpgu->clk)) |
259 | return PTR_ERR(ptr: arcpgu->clk); |
260 | |
261 | ret = drmm_mode_config_init(dev: drm); |
262 | if (ret) |
263 | return ret; |
264 | |
265 | drm->mode_config.min_width = 0; |
266 | drm->mode_config.min_height = 0; |
267 | drm->mode_config.max_width = 1920; |
268 | drm->mode_config.max_height = 1080; |
269 | drm->mode_config.funcs = &arcpgu_drm_modecfg_funcs; |
270 | |
271 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
272 | arcpgu->regs = devm_ioremap_resource(dev: &pdev->dev, res); |
273 | if (IS_ERR(ptr: arcpgu->regs)) |
274 | return PTR_ERR(ptr: arcpgu->regs); |
275 | |
276 | dev_info(drm->dev, "arc_pgu ID: 0x%x\n" , |
277 | arc_pgu_read(arcpgu, ARCPGU_REG_ID)); |
278 | |
279 | /* Get the optional framebuffer memory resource */ |
280 | ret = of_reserved_mem_device_init(dev: drm->dev); |
281 | if (ret && ret != -ENODEV) |
282 | return ret; |
283 | |
284 | if (dma_set_mask_and_coherent(dev: drm->dev, DMA_BIT_MASK(32))) |
285 | return -ENODEV; |
286 | |
287 | /* |
288 | * There is only one output port inside each device. It is linked with |
289 | * encoder endpoint. |
290 | */ |
291 | endpoint_node = of_graph_get_next_endpoint(parent: pdev->dev.of_node, NULL); |
292 | if (endpoint_node) { |
293 | encoder_node = of_graph_get_remote_port_parent(node: endpoint_node); |
294 | of_node_put(node: endpoint_node); |
295 | } else { |
296 | connector = &arcpgu->sim_conn; |
297 | dev_info(drm->dev, "no encoder found. Assumed virtual LCD on simulation platform\n" ); |
298 | ret = arcpgu_drm_sim_init(drm, connector); |
299 | if (ret < 0) |
300 | return ret; |
301 | } |
302 | |
303 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &arcpgu->pipe, funcs: &arc_pgu_pipe_funcs, |
304 | formats: arc_pgu_supported_formats, |
305 | ARRAY_SIZE(arc_pgu_supported_formats), |
306 | NULL, connector); |
307 | if (ret) |
308 | return ret; |
309 | |
310 | if (encoder_node) { |
311 | struct drm_bridge *bridge; |
312 | |
313 | /* Locate drm bridge from the hdmi encoder DT node */ |
314 | bridge = of_drm_find_bridge(np: encoder_node); |
315 | if (!bridge) |
316 | return -EPROBE_DEFER; |
317 | |
318 | ret = drm_simple_display_pipe_attach_bridge(pipe: &arcpgu->pipe, bridge); |
319 | if (ret) |
320 | return ret; |
321 | } |
322 | |
323 | drm_mode_config_reset(dev: drm); |
324 | drm_kms_helper_poll_init(dev: drm); |
325 | |
326 | platform_set_drvdata(pdev, data: drm); |
327 | return 0; |
328 | } |
329 | |
330 | static int arcpgu_unload(struct drm_device *drm) |
331 | { |
332 | drm_kms_helper_poll_fini(dev: drm); |
333 | drm_atomic_helper_shutdown(dev: drm); |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | #ifdef CONFIG_DEBUG_FS |
339 | static int arcpgu_show_pxlclock(struct seq_file *m, void *arg) |
340 | { |
341 | struct drm_info_node *node = (struct drm_info_node *)m->private; |
342 | struct drm_device *drm = node->minor->dev; |
343 | struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(drm); |
344 | unsigned long clkrate = clk_get_rate(clk: arcpgu->clk); |
345 | unsigned long mode_clock = arcpgu->pipe.crtc.mode.crtc_clock * 1000; |
346 | |
347 | seq_printf(m, fmt: "hw : %lu\n" , clkrate); |
348 | seq_printf(m, fmt: "mode: %lu\n" , mode_clock); |
349 | return 0; |
350 | } |
351 | |
352 | static struct drm_info_list arcpgu_debugfs_list[] = { |
353 | { "clocks" , arcpgu_show_pxlclock, 0 }, |
354 | }; |
355 | |
356 | static void arcpgu_debugfs_init(struct drm_minor *minor) |
357 | { |
358 | drm_debugfs_create_files(files: arcpgu_debugfs_list, |
359 | ARRAY_SIZE(arcpgu_debugfs_list), |
360 | root: minor->debugfs_root, minor); |
361 | } |
362 | #endif |
363 | |
364 | static const struct drm_driver arcpgu_drm_driver = { |
365 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, |
366 | .name = "arcpgu" , |
367 | .desc = "ARC PGU Controller" , |
368 | .date = "20160219" , |
369 | .major = 1, |
370 | .minor = 0, |
371 | .patchlevel = 0, |
372 | .fops = &arcpgu_drm_ops, |
373 | DRM_GEM_DMA_DRIVER_OPS, |
374 | #ifdef CONFIG_DEBUG_FS |
375 | .debugfs_init = arcpgu_debugfs_init, |
376 | #endif |
377 | }; |
378 | |
379 | static int arcpgu_probe(struct platform_device *pdev) |
380 | { |
381 | struct arcpgu_drm_private *arcpgu; |
382 | int ret; |
383 | |
384 | arcpgu = devm_drm_dev_alloc(&pdev->dev, &arcpgu_drm_driver, |
385 | struct arcpgu_drm_private, drm); |
386 | if (IS_ERR(ptr: arcpgu)) |
387 | return PTR_ERR(ptr: arcpgu); |
388 | |
389 | ret = arcpgu_load(arcpgu); |
390 | if (ret) |
391 | return ret; |
392 | |
393 | ret = drm_dev_register(dev: &arcpgu->drm, flags: 0); |
394 | if (ret) |
395 | goto err_unload; |
396 | |
397 | drm_fbdev_dma_setup(dev: &arcpgu->drm, preferred_bpp: 16); |
398 | |
399 | return 0; |
400 | |
401 | err_unload: |
402 | arcpgu_unload(drm: &arcpgu->drm); |
403 | |
404 | return ret; |
405 | } |
406 | |
407 | static int arcpgu_remove(struct platform_device *pdev) |
408 | { |
409 | struct drm_device *drm = platform_get_drvdata(pdev); |
410 | |
411 | drm_dev_unregister(dev: drm); |
412 | arcpgu_unload(drm); |
413 | |
414 | return 0; |
415 | } |
416 | |
417 | static const struct of_device_id arcpgu_of_table[] = { |
418 | {.compatible = "snps,arcpgu" }, |
419 | {} |
420 | }; |
421 | |
422 | MODULE_DEVICE_TABLE(of, arcpgu_of_table); |
423 | |
424 | static struct platform_driver arcpgu_platform_driver = { |
425 | .probe = arcpgu_probe, |
426 | .remove = arcpgu_remove, |
427 | .driver = { |
428 | .name = "arcpgu" , |
429 | .of_match_table = arcpgu_of_table, |
430 | }, |
431 | }; |
432 | |
433 | drm_module_platform_driver(arcpgu_platform_driver); |
434 | |
435 | MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>" ); |
436 | MODULE_DESCRIPTION("ARC PGU DRM driver" ); |
437 | MODULE_LICENSE("GPL" ); |
438 | |