1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Ingenic JZ47xx KMS driver |
4 | // |
5 | // Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> |
6 | |
7 | #include "ingenic-drm.h" |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/component.h> |
11 | #include <linux/clk.h> |
12 | #include <linux/dma-mapping.h> |
13 | #include <linux/io.h> |
14 | #include <linux/media-bus-format.h> |
15 | #include <linux/module.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_reserved_mem.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pm.h> |
21 | #include <linux/regmap.h> |
22 | |
23 | #include <drm/drm_atomic.h> |
24 | #include <drm/drm_atomic_helper.h> |
25 | #include <drm/drm_bridge.h> |
26 | #include <drm/drm_bridge_connector.h> |
27 | #include <drm/drm_color_mgmt.h> |
28 | #include <drm/drm_crtc.h> |
29 | #include <drm/drm_damage_helper.h> |
30 | #include <drm/drm_drv.h> |
31 | #include <drm/drm_encoder.h> |
32 | #include <drm/drm_gem_dma_helper.h> |
33 | #include <drm/drm_fb_dma_helper.h> |
34 | #include <drm/drm_fbdev_generic.h> |
35 | #include <drm/drm_fourcc.h> |
36 | #include <drm/drm_framebuffer.h> |
37 | #include <drm/drm_gem_atomic_helper.h> |
38 | #include <drm/drm_gem_framebuffer_helper.h> |
39 | #include <drm/drm_managed.h> |
40 | #include <drm/drm_of.h> |
41 | #include <drm/drm_panel.h> |
42 | #include <drm/drm_plane.h> |
43 | #include <drm/drm_probe_helper.h> |
44 | #include <drm/drm_vblank.h> |
45 | |
46 | #define HWDESC_PALETTE 2 |
47 | |
48 | struct ingenic_dma_hwdesc { |
49 | u32 next; |
50 | u32 addr; |
51 | u32 id; |
52 | u32 cmd; |
53 | /* extended hw descriptor for jz4780 */ |
54 | u32 offsize; |
55 | u32 pagewidth; |
56 | u32 cpos; |
57 | u32 dessize; |
58 | } __aligned(16); |
59 | |
60 | struct ingenic_dma_hwdescs { |
61 | struct ingenic_dma_hwdesc hwdesc[3]; |
62 | u16 palette[256] __aligned(16); |
63 | }; |
64 | |
65 | struct jz_soc_info { |
66 | bool needs_dev_clk; |
67 | bool has_osd; |
68 | bool has_alpha; |
69 | bool map_noncoherent; |
70 | bool use_extended_hwdesc; |
71 | bool plane_f0_not_working; |
72 | u32 max_burst; |
73 | unsigned int max_width, max_height; |
74 | const u32 *formats_f0, *formats_f1; |
75 | unsigned int num_formats_f0, num_formats_f1; |
76 | }; |
77 | |
78 | struct ingenic_drm_private_state { |
79 | struct drm_private_state base; |
80 | bool use_palette; |
81 | }; |
82 | |
83 | struct ingenic_drm { |
84 | struct drm_device drm; |
85 | /* |
86 | * f1 (aka. foreground1) is our primary plane, on top of which |
87 | * f0 (aka. foreground0) can be overlayed. Z-order is fixed in |
88 | * hardware and cannot be changed. |
89 | */ |
90 | struct drm_plane f0, f1, *ipu_plane; |
91 | struct drm_crtc crtc; |
92 | |
93 | struct device *dev; |
94 | struct regmap *map; |
95 | struct clk *lcd_clk, *pix_clk; |
96 | const struct jz_soc_info *soc_info; |
97 | |
98 | struct ingenic_dma_hwdescs *dma_hwdescs; |
99 | dma_addr_t dma_hwdescs_phys; |
100 | |
101 | bool panel_is_sharp; |
102 | bool no_vblank; |
103 | |
104 | /* |
105 | * clk_mutex is used to synchronize the pixel clock rate update with |
106 | * the VBLANK. When the pixel clock's parent clock needs to be updated, |
107 | * clock_nb's notifier function will lock the mutex, then wait until the |
108 | * next VBLANK. At that point, the parent clock's rate can be updated, |
109 | * and the mutex is then unlocked. If an atomic commit happens in the |
110 | * meantime, it will lock on the mutex, effectively waiting until the |
111 | * clock update process finishes. Finally, the pixel clock's rate will |
112 | * be recomputed when the mutex has been released, in the pending atomic |
113 | * commit, or a future one. |
114 | */ |
115 | struct mutex clk_mutex; |
116 | bool update_clk_rate; |
117 | struct notifier_block clock_nb; |
118 | |
119 | struct drm_private_obj private_obj; |
120 | }; |
121 | |
122 | struct ingenic_drm_bridge { |
123 | struct drm_encoder encoder; |
124 | struct drm_bridge bridge, *next_bridge; |
125 | |
126 | struct drm_bus_cfg bus_cfg; |
127 | }; |
128 | |
129 | static inline struct ingenic_drm_bridge * |
130 | to_ingenic_drm_bridge(struct drm_encoder *encoder) |
131 | { |
132 | return container_of(encoder, struct ingenic_drm_bridge, encoder); |
133 | } |
134 | |
135 | static inline struct ingenic_drm_private_state * |
136 | to_ingenic_drm_priv_state(struct drm_private_state *state) |
137 | { |
138 | return container_of(state, struct ingenic_drm_private_state, base); |
139 | } |
140 | |
141 | static struct ingenic_drm_private_state * |
142 | ingenic_drm_get_priv_state(struct ingenic_drm *priv, struct drm_atomic_state *state) |
143 | { |
144 | struct drm_private_state *priv_state; |
145 | |
146 | priv_state = drm_atomic_get_private_obj_state(state, obj: &priv->private_obj); |
147 | if (IS_ERR(ptr: priv_state)) |
148 | return ERR_CAST(ptr: priv_state); |
149 | |
150 | return to_ingenic_drm_priv_state(state: priv_state); |
151 | } |
152 | |
153 | static struct ingenic_drm_private_state * |
154 | ingenic_drm_get_new_priv_state(struct ingenic_drm *priv, struct drm_atomic_state *state) |
155 | { |
156 | struct drm_private_state *priv_state; |
157 | |
158 | priv_state = drm_atomic_get_new_private_obj_state(state, obj: &priv->private_obj); |
159 | if (!priv_state) |
160 | return NULL; |
161 | |
162 | return to_ingenic_drm_priv_state(state: priv_state); |
163 | } |
164 | |
165 | static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg) |
166 | { |
167 | switch (reg) { |
168 | case JZ_REG_LCD_IID: |
169 | case JZ_REG_LCD_SA0: |
170 | case JZ_REG_LCD_FID0: |
171 | case JZ_REG_LCD_CMD0: |
172 | case JZ_REG_LCD_SA1: |
173 | case JZ_REG_LCD_FID1: |
174 | case JZ_REG_LCD_CMD1: |
175 | return false; |
176 | default: |
177 | return true; |
178 | } |
179 | } |
180 | |
181 | static const struct regmap_config ingenic_drm_regmap_config = { |
182 | .reg_bits = 32, |
183 | .val_bits = 32, |
184 | .reg_stride = 4, |
185 | |
186 | .writeable_reg = ingenic_drm_writeable_reg, |
187 | }; |
188 | |
189 | static inline struct ingenic_drm *drm_device_get_priv(struct drm_device *drm) |
190 | { |
191 | return container_of(drm, struct ingenic_drm, drm); |
192 | } |
193 | |
194 | static inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc) |
195 | { |
196 | return container_of(crtc, struct ingenic_drm, crtc); |
197 | } |
198 | |
199 | static inline struct ingenic_drm *drm_nb_get_priv(struct notifier_block *nb) |
200 | { |
201 | return container_of(nb, struct ingenic_drm, clock_nb); |
202 | } |
203 | |
204 | static inline dma_addr_t dma_hwdesc_addr(const struct ingenic_drm *priv, |
205 | unsigned int idx) |
206 | { |
207 | u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc[idx]); |
208 | |
209 | return priv->dma_hwdescs_phys + offset; |
210 | } |
211 | |
212 | static int ingenic_drm_update_pixclk(struct notifier_block *nb, |
213 | unsigned long action, |
214 | void *data) |
215 | { |
216 | struct ingenic_drm *priv = drm_nb_get_priv(nb); |
217 | |
218 | switch (action) { |
219 | case PRE_RATE_CHANGE: |
220 | mutex_lock(&priv->clk_mutex); |
221 | priv->update_clk_rate = true; |
222 | drm_crtc_wait_one_vblank(crtc: &priv->crtc); |
223 | return NOTIFY_OK; |
224 | default: |
225 | mutex_unlock(lock: &priv->clk_mutex); |
226 | return NOTIFY_OK; |
227 | } |
228 | } |
229 | |
230 | static void ingenic_drm_bridge_atomic_enable(struct drm_bridge *bridge, |
231 | struct drm_bridge_state *old_bridge_state) |
232 | { |
233 | struct ingenic_drm *priv = drm_device_get_priv(drm: bridge->dev); |
234 | |
235 | regmap_write(map: priv->map, JZ_REG_LCD_STATE, val: 0); |
236 | |
237 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, |
238 | JZ_LCD_CTRL_ENABLE | JZ_LCD_CTRL_DISABLE, |
239 | JZ_LCD_CTRL_ENABLE); |
240 | } |
241 | |
242 | static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, |
243 | struct drm_atomic_state *state) |
244 | { |
245 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
246 | struct ingenic_drm_private_state *priv_state; |
247 | unsigned int next_id; |
248 | |
249 | priv_state = ingenic_drm_get_priv_state(priv, state); |
250 | if (WARN_ON(IS_ERR(priv_state))) |
251 | return; |
252 | |
253 | /* Set addresses of our DMA descriptor chains */ |
254 | next_id = priv_state->use_palette ? HWDESC_PALETTE : 0; |
255 | regmap_write(map: priv->map, JZ_REG_LCD_DA0, val: dma_hwdesc_addr(priv, idx: next_id)); |
256 | regmap_write(map: priv->map, JZ_REG_LCD_DA1, val: dma_hwdesc_addr(priv, idx: 1)); |
257 | |
258 | drm_crtc_vblank_on(crtc); |
259 | } |
260 | |
261 | static void ingenic_drm_bridge_atomic_disable(struct drm_bridge *bridge, |
262 | struct drm_bridge_state *old_bridge_state) |
263 | { |
264 | struct ingenic_drm *priv = drm_device_get_priv(drm: bridge->dev); |
265 | unsigned int var; |
266 | |
267 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, |
268 | JZ_LCD_CTRL_DISABLE, JZ_LCD_CTRL_DISABLE); |
269 | |
270 | regmap_read_poll_timeout(priv->map, JZ_REG_LCD_STATE, var, |
271 | var & JZ_LCD_STATE_DISABLED, |
272 | 1000, 0); |
273 | } |
274 | |
275 | static void ingenic_drm_crtc_atomic_disable(struct drm_crtc *crtc, |
276 | struct drm_atomic_state *state) |
277 | { |
278 | drm_crtc_vblank_off(crtc); |
279 | } |
280 | |
281 | static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv, |
282 | struct drm_display_mode *mode) |
283 | { |
284 | unsigned int vpe, vds, vde, vt, hpe, hds, hde, ht; |
285 | |
286 | vpe = mode->crtc_vsync_end - mode->crtc_vsync_start; |
287 | vds = mode->crtc_vtotal - mode->crtc_vsync_start; |
288 | vde = vds + mode->crtc_vdisplay; |
289 | vt = vde + mode->crtc_vsync_start - mode->crtc_vdisplay; |
290 | |
291 | hpe = mode->crtc_hsync_end - mode->crtc_hsync_start; |
292 | hds = mode->crtc_htotal - mode->crtc_hsync_start; |
293 | hde = hds + mode->crtc_hdisplay; |
294 | ht = hde + mode->crtc_hsync_start - mode->crtc_hdisplay; |
295 | |
296 | regmap_write(map: priv->map, JZ_REG_LCD_VSYNC, |
297 | val: 0 << JZ_LCD_VSYNC_VPS_OFFSET | |
298 | vpe << JZ_LCD_VSYNC_VPE_OFFSET); |
299 | |
300 | regmap_write(map: priv->map, JZ_REG_LCD_HSYNC, |
301 | val: 0 << JZ_LCD_HSYNC_HPS_OFFSET | |
302 | hpe << JZ_LCD_HSYNC_HPE_OFFSET); |
303 | |
304 | regmap_write(map: priv->map, JZ_REG_LCD_VAT, |
305 | val: ht << JZ_LCD_VAT_HT_OFFSET | |
306 | vt << JZ_LCD_VAT_VT_OFFSET); |
307 | |
308 | regmap_write(map: priv->map, JZ_REG_LCD_DAH, |
309 | val: hds << JZ_LCD_DAH_HDS_OFFSET | |
310 | hde << JZ_LCD_DAH_HDE_OFFSET); |
311 | regmap_write(map: priv->map, JZ_REG_LCD_DAV, |
312 | val: vds << JZ_LCD_DAV_VDS_OFFSET | |
313 | vde << JZ_LCD_DAV_VDE_OFFSET); |
314 | |
315 | if (priv->panel_is_sharp) { |
316 | regmap_write(map: priv->map, JZ_REG_LCD_PS, val: hde << 16 | (hde + 1)); |
317 | regmap_write(map: priv->map, JZ_REG_LCD_CLS, val: hde << 16 | (hde + 1)); |
318 | regmap_write(map: priv->map, JZ_REG_LCD_SPL, val: hpe << 16 | (hpe + 1)); |
319 | regmap_write(map: priv->map, JZ_REG_LCD_REV, val: mode->htotal << 16); |
320 | } |
321 | |
322 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, |
323 | JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_MASK, |
324 | JZ_LCD_CTRL_OFUP | priv->soc_info->max_burst); |
325 | |
326 | /* |
327 | * IPU restart - specify how much time the LCDC will wait before |
328 | * transferring a new frame from the IPU. The value is the one |
329 | * suggested in the programming manual. |
330 | */ |
331 | regmap_write(map: priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN | |
332 | (ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB); |
333 | } |
334 | |
335 | static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, |
336 | struct drm_atomic_state *state) |
337 | { |
338 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
339 | crtc); |
340 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
341 | struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL; |
342 | |
343 | if (crtc_state->gamma_lut && |
344 | drm_color_lut_size(blob: crtc_state->gamma_lut) != ARRAY_SIZE(priv->dma_hwdescs->palette)) { |
345 | dev_dbg(priv->dev, "Invalid palette size\n" ); |
346 | return -EINVAL; |
347 | } |
348 | |
349 | if (drm_atomic_crtc_needs_modeset(state: crtc_state) && priv->soc_info->has_osd) { |
350 | f1_state = drm_atomic_get_plane_state(state: crtc_state->state, |
351 | plane: &priv->f1); |
352 | if (IS_ERR(ptr: f1_state)) |
353 | return PTR_ERR(ptr: f1_state); |
354 | |
355 | f0_state = drm_atomic_get_plane_state(state: crtc_state->state, |
356 | plane: &priv->f0); |
357 | if (IS_ERR(ptr: f0_state)) |
358 | return PTR_ERR(ptr: f0_state); |
359 | |
360 | if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) { |
361 | ipu_state = drm_atomic_get_plane_state(state: crtc_state->state, |
362 | plane: priv->ipu_plane); |
363 | if (IS_ERR(ptr: ipu_state)) |
364 | return PTR_ERR(ptr: ipu_state); |
365 | |
366 | /* IPU and F1 planes cannot be enabled at the same time. */ |
367 | if (f1_state->fb && ipu_state->fb) { |
368 | dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n" ); |
369 | return -EINVAL; |
370 | } |
371 | } |
372 | |
373 | /* If all the planes are disabled, we won't get a VBLANK IRQ */ |
374 | priv->no_vblank = !f1_state->fb && !f0_state->fb && |
375 | !(ipu_state && ipu_state->fb); |
376 | } |
377 | |
378 | return 0; |
379 | } |
380 | |
381 | static enum drm_mode_status |
382 | ingenic_drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) |
383 | { |
384 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
385 | long rate; |
386 | |
387 | if (mode->hdisplay > priv->soc_info->max_width) |
388 | return MODE_BAD_HVALUE; |
389 | if (mode->vdisplay > priv->soc_info->max_height) |
390 | return MODE_BAD_VVALUE; |
391 | |
392 | rate = clk_round_rate(clk: priv->pix_clk, rate: mode->clock * 1000); |
393 | if (rate < 0) |
394 | return MODE_CLOCK_RANGE; |
395 | |
396 | return MODE_OK; |
397 | } |
398 | |
399 | static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc, |
400 | struct drm_atomic_state *state) |
401 | { |
402 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
403 | crtc); |
404 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
405 | u32 ctrl = 0; |
406 | |
407 | if (priv->soc_info->has_osd && |
408 | drm_atomic_crtc_needs_modeset(state: crtc_state)) { |
409 | /* |
410 | * If IPU plane is enabled, enable IPU as source for the F1 |
411 | * plane; otherwise use regular DMA. |
412 | */ |
413 | if (priv->ipu_plane && priv->ipu_plane->state->fb) |
414 | ctrl |= JZ_LCD_OSDCTRL_IPU; |
415 | |
416 | regmap_update_bits(map: priv->map, JZ_REG_LCD_OSDCTRL, |
417 | JZ_LCD_OSDCTRL_IPU, val: ctrl); |
418 | } |
419 | } |
420 | |
421 | static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, |
422 | struct drm_atomic_state *state) |
423 | { |
424 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
425 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
426 | crtc); |
427 | struct drm_pending_vblank_event *event = crtc_state->event; |
428 | |
429 | if (drm_atomic_crtc_needs_modeset(state: crtc_state)) { |
430 | ingenic_drm_crtc_update_timings(priv, mode: &crtc_state->adjusted_mode); |
431 | priv->update_clk_rate = true; |
432 | } |
433 | |
434 | if (priv->update_clk_rate) { |
435 | mutex_lock(&priv->clk_mutex); |
436 | clk_set_rate(clk: priv->pix_clk, |
437 | rate: crtc_state->adjusted_mode.crtc_clock * 1000); |
438 | priv->update_clk_rate = false; |
439 | mutex_unlock(lock: &priv->clk_mutex); |
440 | } |
441 | |
442 | if (event) { |
443 | crtc_state->event = NULL; |
444 | |
445 | spin_lock_irq(lock: &crtc->dev->event_lock); |
446 | if (drm_crtc_vblank_get(crtc) == 0) |
447 | drm_crtc_arm_vblank_event(crtc, e: event); |
448 | else |
449 | drm_crtc_send_vblank_event(crtc, e: event); |
450 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
451 | } |
452 | } |
453 | |
454 | static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, |
455 | struct drm_atomic_state *state) |
456 | { |
457 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, |
458 | plane); |
459 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
460 | plane); |
461 | struct ingenic_drm *priv = drm_device_get_priv(drm: plane->dev); |
462 | struct ingenic_drm_private_state *priv_state; |
463 | struct drm_crtc_state *crtc_state; |
464 | struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc; |
465 | int ret; |
466 | |
467 | if (!crtc) |
468 | return 0; |
469 | |
470 | if (priv->soc_info->plane_f0_not_working && plane == &priv->f0) |
471 | return -EINVAL; |
472 | |
473 | crtc_state = drm_atomic_get_existing_crtc_state(state, |
474 | crtc); |
475 | if (WARN_ON(!crtc_state)) |
476 | return -EINVAL; |
477 | |
478 | priv_state = ingenic_drm_get_priv_state(priv, state); |
479 | if (IS_ERR(ptr: priv_state)) |
480 | return PTR_ERR(ptr: priv_state); |
481 | |
482 | ret = drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state, |
483 | DRM_PLANE_NO_SCALING, |
484 | DRM_PLANE_NO_SCALING, |
485 | can_position: priv->soc_info->has_osd, |
486 | can_update_disabled: true); |
487 | if (ret) |
488 | return ret; |
489 | |
490 | /* |
491 | * If OSD is not available, check that the width/height match. |
492 | * Note that state->src_* are in 16.16 fixed-point format. |
493 | */ |
494 | if (!priv->soc_info->has_osd && |
495 | (new_plane_state->src_x != 0 || |
496 | (new_plane_state->src_w >> 16) != new_plane_state->crtc_w || |
497 | (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)) |
498 | return -EINVAL; |
499 | |
500 | priv_state->use_palette = new_plane_state->fb && |
501 | new_plane_state->fb->format->format == DRM_FORMAT_C8; |
502 | |
503 | /* |
504 | * Require full modeset if enabling or disabling a plane, or changing |
505 | * its position, size or depth. |
506 | */ |
507 | if (priv->soc_info->has_osd && |
508 | (!old_plane_state->fb || !new_plane_state->fb || |
509 | old_plane_state->crtc_x != new_plane_state->crtc_x || |
510 | old_plane_state->crtc_y != new_plane_state->crtc_y || |
511 | old_plane_state->crtc_w != new_plane_state->crtc_w || |
512 | old_plane_state->crtc_h != new_plane_state->crtc_h || |
513 | old_plane_state->fb->format->format != new_plane_state->fb->format->format)) |
514 | crtc_state->mode_changed = true; |
515 | |
516 | if (priv->soc_info->map_noncoherent) |
517 | drm_atomic_helper_check_plane_damage(state, plane_state: new_plane_state); |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | static void ingenic_drm_plane_enable(struct ingenic_drm *priv, |
523 | struct drm_plane *plane) |
524 | { |
525 | unsigned int en_bit; |
526 | |
527 | if (priv->soc_info->has_osd) { |
528 | if (plane != &priv->f0) |
529 | en_bit = JZ_LCD_OSDC_F1EN; |
530 | else |
531 | en_bit = JZ_LCD_OSDC_F0EN; |
532 | |
533 | regmap_set_bits(map: priv->map, JZ_REG_LCD_OSDC, bits: en_bit); |
534 | } |
535 | } |
536 | |
537 | void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane) |
538 | { |
539 | struct ingenic_drm *priv = dev_get_drvdata(dev); |
540 | unsigned int en_bit; |
541 | |
542 | if (priv->soc_info->has_osd) { |
543 | if (plane != &priv->f0) |
544 | en_bit = JZ_LCD_OSDC_F1EN; |
545 | else |
546 | en_bit = JZ_LCD_OSDC_F0EN; |
547 | |
548 | regmap_clear_bits(map: priv->map, JZ_REG_LCD_OSDC, bits: en_bit); |
549 | } |
550 | } |
551 | |
552 | static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, |
553 | struct drm_atomic_state *state) |
554 | { |
555 | struct ingenic_drm *priv = drm_device_get_priv(drm: plane->dev); |
556 | |
557 | ingenic_drm_plane_disable(dev: priv->dev, plane); |
558 | } |
559 | |
560 | void ingenic_drm_plane_config(struct device *dev, |
561 | struct drm_plane *plane, u32 fourcc) |
562 | { |
563 | struct ingenic_drm *priv = dev_get_drvdata(dev); |
564 | struct drm_plane_state *state = plane->state; |
565 | unsigned int xy_reg, size_reg; |
566 | unsigned int ctrl = 0; |
567 | |
568 | ingenic_drm_plane_enable(priv, plane); |
569 | |
570 | if (priv->soc_info->has_osd && plane != &priv->f0) { |
571 | switch (fourcc) { |
572 | case DRM_FORMAT_XRGB1555: |
573 | ctrl |= JZ_LCD_OSDCTRL_RGB555; |
574 | fallthrough; |
575 | case DRM_FORMAT_RGB565: |
576 | ctrl |= JZ_LCD_OSDCTRL_BPP_15_16; |
577 | break; |
578 | case DRM_FORMAT_RGB888: |
579 | ctrl |= JZ_LCD_OSDCTRL_BPP_24_COMP; |
580 | break; |
581 | case DRM_FORMAT_XRGB8888: |
582 | ctrl |= JZ_LCD_OSDCTRL_BPP_18_24; |
583 | break; |
584 | case DRM_FORMAT_XRGB2101010: |
585 | ctrl |= JZ_LCD_OSDCTRL_BPP_30; |
586 | break; |
587 | } |
588 | |
589 | regmap_update_bits(map: priv->map, JZ_REG_LCD_OSDCTRL, |
590 | JZ_LCD_OSDCTRL_BPP_MASK, val: ctrl); |
591 | } else { |
592 | switch (fourcc) { |
593 | case DRM_FORMAT_C8: |
594 | ctrl |= JZ_LCD_CTRL_BPP_8; |
595 | break; |
596 | case DRM_FORMAT_XRGB1555: |
597 | ctrl |= JZ_LCD_CTRL_RGB555; |
598 | fallthrough; |
599 | case DRM_FORMAT_RGB565: |
600 | ctrl |= JZ_LCD_CTRL_BPP_15_16; |
601 | break; |
602 | case DRM_FORMAT_RGB888: |
603 | ctrl |= JZ_LCD_CTRL_BPP_24_COMP; |
604 | break; |
605 | case DRM_FORMAT_XRGB8888: |
606 | ctrl |= JZ_LCD_CTRL_BPP_18_24; |
607 | break; |
608 | case DRM_FORMAT_XRGB2101010: |
609 | ctrl |= JZ_LCD_CTRL_BPP_30; |
610 | break; |
611 | } |
612 | |
613 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, |
614 | JZ_LCD_CTRL_BPP_MASK, val: ctrl); |
615 | } |
616 | |
617 | if (priv->soc_info->has_osd) { |
618 | if (plane != &priv->f0) { |
619 | xy_reg = JZ_REG_LCD_XYP1; |
620 | size_reg = JZ_REG_LCD_SIZE1; |
621 | } else { |
622 | xy_reg = JZ_REG_LCD_XYP0; |
623 | size_reg = JZ_REG_LCD_SIZE0; |
624 | } |
625 | |
626 | regmap_write(map: priv->map, reg: xy_reg, |
627 | val: state->crtc_x << JZ_LCD_XYP01_XPOS_LSB | |
628 | state->crtc_y << JZ_LCD_XYP01_YPOS_LSB); |
629 | regmap_write(map: priv->map, reg: size_reg, |
630 | val: state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB | |
631 | state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); |
632 | } |
633 | } |
634 | |
635 | bool ingenic_drm_map_noncoherent(const struct device *dev) |
636 | { |
637 | const struct ingenic_drm *priv = dev_get_drvdata(dev); |
638 | |
639 | return priv->soc_info->map_noncoherent; |
640 | } |
641 | |
642 | static void ingenic_drm_update_palette(struct ingenic_drm *priv, |
643 | const struct drm_color_lut *lut) |
644 | { |
645 | unsigned int i; |
646 | |
647 | for (i = 0; i < ARRAY_SIZE(priv->dma_hwdescs->palette); i++) { |
648 | u16 color = drm_color_lut_extract(user_input: lut[i].red, bit_precision: 5) << 11 |
649 | | drm_color_lut_extract(user_input: lut[i].green, bit_precision: 6) << 5 |
650 | | drm_color_lut_extract(user_input: lut[i].blue, bit_precision: 5); |
651 | |
652 | priv->dma_hwdescs->palette[i] = color; |
653 | } |
654 | } |
655 | |
656 | static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, |
657 | struct drm_atomic_state *state) |
658 | { |
659 | struct ingenic_drm *priv = drm_device_get_priv(drm: plane->dev); |
660 | struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, plane); |
661 | struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, plane); |
662 | unsigned int width, height, cpp, next_id, plane_id; |
663 | struct ingenic_drm_private_state *priv_state; |
664 | struct drm_crtc_state *crtc_state; |
665 | struct ingenic_dma_hwdesc *hwdesc; |
666 | dma_addr_t addr; |
667 | u32 fourcc; |
668 | |
669 | if (newstate && newstate->fb) { |
670 | if (priv->soc_info->map_noncoherent) |
671 | drm_fb_dma_sync_non_coherent(drm: &priv->drm, old_state: oldstate, state: newstate); |
672 | |
673 | crtc_state = newstate->crtc->state; |
674 | plane_id = !!(priv->soc_info->has_osd && plane != &priv->f0); |
675 | |
676 | addr = drm_fb_dma_get_gem_addr(fb: newstate->fb, state: newstate, plane: 0); |
677 | width = newstate->src_w >> 16; |
678 | height = newstate->src_h >> 16; |
679 | cpp = newstate->fb->format->cpp[0]; |
680 | |
681 | priv_state = ingenic_drm_get_new_priv_state(priv, state); |
682 | next_id = (priv_state && priv_state->use_palette) ? HWDESC_PALETTE : plane_id; |
683 | |
684 | hwdesc = &priv->dma_hwdescs->hwdesc[plane_id]; |
685 | hwdesc->addr = addr; |
686 | hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); |
687 | hwdesc->next = dma_hwdesc_addr(priv, idx: next_id); |
688 | |
689 | if (priv->soc_info->use_extended_hwdesc) { |
690 | hwdesc->cmd |= JZ_LCD_CMD_FRM_ENABLE; |
691 | |
692 | /* Extended 8-byte descriptor */ |
693 | hwdesc->cpos = 0; |
694 | hwdesc->offsize = 0; |
695 | hwdesc->pagewidth = 0; |
696 | |
697 | switch (newstate->fb->format->format) { |
698 | case DRM_FORMAT_XRGB1555: |
699 | hwdesc->cpos |= JZ_LCD_CPOS_RGB555; |
700 | fallthrough; |
701 | case DRM_FORMAT_RGB565: |
702 | hwdesc->cpos |= JZ_LCD_CPOS_BPP_15_16; |
703 | break; |
704 | case DRM_FORMAT_XRGB8888: |
705 | hwdesc->cpos |= JZ_LCD_CPOS_BPP_18_24; |
706 | break; |
707 | } |
708 | hwdesc->cpos |= (JZ_LCD_CPOS_COEFFICIENT_1 << |
709 | JZ_LCD_CPOS_COEFFICIENT_OFFSET); |
710 | hwdesc->dessize = |
711 | (0xff << JZ_LCD_DESSIZE_ALPHA_OFFSET) | |
712 | FIELD_PREP(JZ_LCD_DESSIZE_HEIGHT_MASK, height - 1) | |
713 | FIELD_PREP(JZ_LCD_DESSIZE_WIDTH_MASK, width - 1); |
714 | } |
715 | |
716 | if (drm_atomic_crtc_needs_modeset(state: crtc_state)) { |
717 | fourcc = newstate->fb->format->format; |
718 | |
719 | ingenic_drm_plane_config(dev: priv->dev, plane, fourcc); |
720 | |
721 | crtc_state->color_mgmt_changed = fourcc == DRM_FORMAT_C8; |
722 | } |
723 | |
724 | if (crtc_state->color_mgmt_changed) |
725 | ingenic_drm_update_palette(priv, lut: crtc_state->gamma_lut->data); |
726 | } |
727 | } |
728 | |
729 | static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder, |
730 | struct drm_crtc_state *crtc_state, |
731 | struct drm_connector_state *conn_state) |
732 | { |
733 | struct ingenic_drm *priv = drm_device_get_priv(drm: encoder->dev); |
734 | struct drm_display_mode *mode = &crtc_state->adjusted_mode; |
735 | struct ingenic_drm_bridge *bridge = to_ingenic_drm_bridge(encoder); |
736 | unsigned int cfg, rgbcfg = 0; |
737 | |
738 | priv->panel_is_sharp = bridge->bus_cfg.flags & DRM_BUS_FLAG_SHARP_SIGNALS; |
739 | |
740 | if (priv->panel_is_sharp) { |
741 | cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY; |
742 | } else { |
743 | cfg = JZ_LCD_CFG_PS_DISABLE | JZ_LCD_CFG_CLS_DISABLE |
744 | | JZ_LCD_CFG_SPL_DISABLE | JZ_LCD_CFG_REV_DISABLE; |
745 | } |
746 | |
747 | if (priv->soc_info->use_extended_hwdesc) |
748 | cfg |= JZ_LCD_CFG_DESCRIPTOR_8; |
749 | |
750 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
751 | cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; |
752 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) |
753 | cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; |
754 | if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW) |
755 | cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; |
756 | if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) |
757 | cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; |
758 | |
759 | if (!priv->panel_is_sharp) { |
760 | if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV) { |
761 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) |
762 | cfg |= JZ_LCD_CFG_MODE_TV_OUT_I; |
763 | else |
764 | cfg |= JZ_LCD_CFG_MODE_TV_OUT_P; |
765 | } else { |
766 | switch (bridge->bus_cfg.format) { |
767 | case MEDIA_BUS_FMT_RGB565_1X16: |
768 | cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT; |
769 | break; |
770 | case MEDIA_BUS_FMT_RGB666_1X18: |
771 | cfg |= JZ_LCD_CFG_MODE_GENERIC_18BIT; |
772 | break; |
773 | case MEDIA_BUS_FMT_RGB888_1X24: |
774 | cfg |= JZ_LCD_CFG_MODE_GENERIC_24BIT; |
775 | break; |
776 | case MEDIA_BUS_FMT_RGB888_3X8_DELTA: |
777 | rgbcfg = JZ_LCD_RGBC_EVEN_GBR | JZ_LCD_RGBC_ODD_RGB; |
778 | fallthrough; |
779 | case MEDIA_BUS_FMT_RGB888_3X8: |
780 | cfg |= JZ_LCD_CFG_MODE_8BIT_SERIAL; |
781 | break; |
782 | default: |
783 | break; |
784 | } |
785 | } |
786 | } |
787 | |
788 | regmap_write(map: priv->map, JZ_REG_LCD_CFG, val: cfg); |
789 | regmap_write(map: priv->map, JZ_REG_LCD_RGBC, val: rgbcfg); |
790 | } |
791 | |
792 | static int ingenic_drm_bridge_attach(struct drm_bridge *bridge, |
793 | enum drm_bridge_attach_flags flags) |
794 | { |
795 | struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(encoder: bridge->encoder); |
796 | |
797 | return drm_bridge_attach(encoder: bridge->encoder, bridge: ib->next_bridge, |
798 | previous: &ib->bridge, flags); |
799 | } |
800 | |
801 | static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge, |
802 | struct drm_bridge_state *bridge_state, |
803 | struct drm_crtc_state *crtc_state, |
804 | struct drm_connector_state *conn_state) |
805 | { |
806 | struct drm_display_mode *mode = &crtc_state->adjusted_mode; |
807 | struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(encoder: bridge->encoder); |
808 | |
809 | ib->bus_cfg = bridge_state->output_bus_cfg; |
810 | |
811 | if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV) |
812 | return 0; |
813 | |
814 | switch (bridge_state->output_bus_cfg.format) { |
815 | case MEDIA_BUS_FMT_RGB888_3X8: |
816 | case MEDIA_BUS_FMT_RGB888_3X8_DELTA: |
817 | /* |
818 | * The LCD controller expects timing values in dot-clock ticks, |
819 | * which is 3x the timing values in pixels when using a 3x8-bit |
820 | * display; but it will count the display area size in pixels |
821 | * either way. Go figure. |
822 | */ |
823 | mode->crtc_clock = mode->clock * 3; |
824 | mode->crtc_hsync_start = mode->hsync_start * 3 - mode->hdisplay * 2; |
825 | mode->crtc_hsync_end = mode->hsync_end * 3 - mode->hdisplay * 2; |
826 | mode->crtc_hdisplay = mode->hdisplay; |
827 | mode->crtc_htotal = mode->htotal * 3 - mode->hdisplay * 2; |
828 | return 0; |
829 | case MEDIA_BUS_FMT_RGB565_1X16: |
830 | case MEDIA_BUS_FMT_RGB666_1X18: |
831 | case MEDIA_BUS_FMT_RGB888_1X24: |
832 | return 0; |
833 | default: |
834 | return -EINVAL; |
835 | } |
836 | } |
837 | |
838 | static u32 * |
839 | ingenic_drm_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, |
840 | struct drm_bridge_state *bridge_state, |
841 | struct drm_crtc_state *crtc_state, |
842 | struct drm_connector_state *conn_state, |
843 | u32 output_fmt, |
844 | unsigned int *num_input_fmts) |
845 | { |
846 | switch (output_fmt) { |
847 | case MEDIA_BUS_FMT_RGB888_1X24: |
848 | case MEDIA_BUS_FMT_RGB666_1X18: |
849 | case MEDIA_BUS_FMT_RGB565_1X16: |
850 | case MEDIA_BUS_FMT_RGB888_3X8: |
851 | case MEDIA_BUS_FMT_RGB888_3X8_DELTA: |
852 | break; |
853 | default: |
854 | *num_input_fmts = 0; |
855 | return NULL; |
856 | } |
857 | |
858 | return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state, |
859 | crtc_state, conn_state, |
860 | output_fmt, |
861 | num_input_fmts); |
862 | } |
863 | |
864 | static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) |
865 | { |
866 | struct ingenic_drm *priv = drm_device_get_priv(drm: arg); |
867 | unsigned int state; |
868 | |
869 | regmap_read(map: priv->map, JZ_REG_LCD_STATE, val: &state); |
870 | |
871 | regmap_update_bits(map: priv->map, JZ_REG_LCD_STATE, |
872 | JZ_LCD_STATE_EOF_IRQ, val: 0); |
873 | |
874 | if (state & JZ_LCD_STATE_EOF_IRQ) |
875 | drm_crtc_handle_vblank(crtc: &priv->crtc); |
876 | |
877 | return IRQ_HANDLED; |
878 | } |
879 | |
880 | static int ingenic_drm_enable_vblank(struct drm_crtc *crtc) |
881 | { |
882 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
883 | |
884 | if (priv->no_vblank) |
885 | return -EINVAL; |
886 | |
887 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, |
888 | JZ_LCD_CTRL_EOF_IRQ, JZ_LCD_CTRL_EOF_IRQ); |
889 | |
890 | return 0; |
891 | } |
892 | |
893 | static void ingenic_drm_disable_vblank(struct drm_crtc *crtc) |
894 | { |
895 | struct ingenic_drm *priv = drm_crtc_get_priv(crtc); |
896 | |
897 | regmap_update_bits(map: priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, val: 0); |
898 | } |
899 | |
900 | static struct drm_framebuffer * |
901 | ingenic_drm_gem_fb_create(struct drm_device *drm, struct drm_file *file, |
902 | const struct drm_mode_fb_cmd2 *mode_cmd) |
903 | { |
904 | struct ingenic_drm *priv = drm_device_get_priv(drm); |
905 | |
906 | if (priv->soc_info->map_noncoherent) |
907 | return drm_gem_fb_create_with_dirty(dev: drm, file, mode_cmd); |
908 | |
909 | return drm_gem_fb_create(dev: drm, file, mode_cmd); |
910 | } |
911 | |
912 | static struct drm_gem_object * |
913 | ingenic_drm_gem_create_object(struct drm_device *drm, size_t size) |
914 | { |
915 | struct ingenic_drm *priv = drm_device_get_priv(drm); |
916 | struct drm_gem_dma_object *obj; |
917 | |
918 | obj = kzalloc(size: sizeof(*obj), GFP_KERNEL); |
919 | if (!obj) |
920 | return ERR_PTR(error: -ENOMEM); |
921 | |
922 | obj->map_noncoherent = priv->soc_info->map_noncoherent; |
923 | |
924 | return &obj->base; |
925 | } |
926 | |
927 | static struct drm_private_state * |
928 | ingenic_drm_duplicate_state(struct drm_private_obj *obj) |
929 | { |
930 | struct ingenic_drm_private_state *state = to_ingenic_drm_priv_state(state: obj->state); |
931 | |
932 | state = kmemdup(p: state, size: sizeof(*state), GFP_KERNEL); |
933 | if (!state) |
934 | return NULL; |
935 | |
936 | __drm_atomic_helper_private_obj_duplicate_state(obj, state: &state->base); |
937 | |
938 | return &state->base; |
939 | } |
940 | |
941 | static void ingenic_drm_destroy_state(struct drm_private_obj *obj, |
942 | struct drm_private_state *state) |
943 | { |
944 | struct ingenic_drm_private_state *priv_state = to_ingenic_drm_priv_state(state); |
945 | |
946 | kfree(objp: priv_state); |
947 | } |
948 | |
949 | DEFINE_DRM_GEM_DMA_FOPS(ingenic_drm_fops); |
950 | |
951 | static const struct drm_driver ingenic_drm_driver_data = { |
952 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, |
953 | .name = "ingenic-drm" , |
954 | .desc = "DRM module for Ingenic SoCs" , |
955 | .date = "20200716" , |
956 | .major = 1, |
957 | .minor = 1, |
958 | .patchlevel = 0, |
959 | |
960 | .fops = &ingenic_drm_fops, |
961 | .gem_create_object = ingenic_drm_gem_create_object, |
962 | DRM_GEM_DMA_DRIVER_OPS, |
963 | }; |
964 | |
965 | static const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { |
966 | .update_plane = drm_atomic_helper_update_plane, |
967 | .disable_plane = drm_atomic_helper_disable_plane, |
968 | .reset = drm_atomic_helper_plane_reset, |
969 | .destroy = drm_plane_cleanup, |
970 | |
971 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
972 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
973 | }; |
974 | |
975 | static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = { |
976 | .set_config = drm_atomic_helper_set_config, |
977 | .page_flip = drm_atomic_helper_page_flip, |
978 | .reset = drm_atomic_helper_crtc_reset, |
979 | .destroy = drm_crtc_cleanup, |
980 | |
981 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
982 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
983 | |
984 | .enable_vblank = ingenic_drm_enable_vblank, |
985 | .disable_vblank = ingenic_drm_disable_vblank, |
986 | }; |
987 | |
988 | static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { |
989 | .atomic_update = ingenic_drm_plane_atomic_update, |
990 | .atomic_check = ingenic_drm_plane_atomic_check, |
991 | .atomic_disable = ingenic_drm_plane_atomic_disable, |
992 | }; |
993 | |
994 | static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = { |
995 | .atomic_enable = ingenic_drm_crtc_atomic_enable, |
996 | .atomic_disable = ingenic_drm_crtc_atomic_disable, |
997 | .atomic_begin = ingenic_drm_crtc_atomic_begin, |
998 | .atomic_flush = ingenic_drm_crtc_atomic_flush, |
999 | .atomic_check = ingenic_drm_crtc_atomic_check, |
1000 | .mode_valid = ingenic_drm_crtc_mode_valid, |
1001 | }; |
1002 | |
1003 | static const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = { |
1004 | .atomic_mode_set = ingenic_drm_encoder_atomic_mode_set, |
1005 | }; |
1006 | |
1007 | static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = { |
1008 | .attach = ingenic_drm_bridge_attach, |
1009 | .atomic_enable = ingenic_drm_bridge_atomic_enable, |
1010 | .atomic_disable = ingenic_drm_bridge_atomic_disable, |
1011 | .atomic_check = ingenic_drm_bridge_atomic_check, |
1012 | .atomic_reset = drm_atomic_helper_bridge_reset, |
1013 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
1014 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
1015 | .atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts, |
1016 | }; |
1017 | |
1018 | static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { |
1019 | .fb_create = ingenic_drm_gem_fb_create, |
1020 | .atomic_check = drm_atomic_helper_check, |
1021 | .atomic_commit = drm_atomic_helper_commit, |
1022 | }; |
1023 | |
1024 | static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { |
1025 | .atomic_commit_tail = drm_atomic_helper_commit_tail, |
1026 | }; |
1027 | |
1028 | static const struct drm_private_state_funcs ingenic_drm_private_state_funcs = { |
1029 | .atomic_duplicate_state = ingenic_drm_duplicate_state, |
1030 | .atomic_destroy_state = ingenic_drm_destroy_state, |
1031 | }; |
1032 | |
1033 | static void ingenic_drm_unbind_all(void *d) |
1034 | { |
1035 | struct ingenic_drm *priv = d; |
1036 | |
1037 | component_unbind_all(parent: priv->dev, data: &priv->drm); |
1038 | } |
1039 | |
1040 | static void __maybe_unused ingenic_drm_release_rmem(void *d) |
1041 | { |
1042 | of_reserved_mem_device_release(dev: d); |
1043 | } |
1044 | |
1045 | static void ingenic_drm_configure_hwdesc(struct ingenic_drm *priv, |
1046 | unsigned int hwdesc, |
1047 | unsigned int next_hwdesc, u32 id) |
1048 | { |
1049 | struct ingenic_dma_hwdesc *desc = &priv->dma_hwdescs->hwdesc[hwdesc]; |
1050 | |
1051 | desc->next = dma_hwdesc_addr(priv, idx: next_hwdesc); |
1052 | desc->id = id; |
1053 | } |
1054 | |
1055 | static void ingenic_drm_configure_hwdesc_palette(struct ingenic_drm *priv) |
1056 | { |
1057 | struct ingenic_dma_hwdesc *desc; |
1058 | |
1059 | ingenic_drm_configure_hwdesc(priv, HWDESC_PALETTE, next_hwdesc: 0, id: 0xc0); |
1060 | |
1061 | desc = &priv->dma_hwdescs->hwdesc[HWDESC_PALETTE]; |
1062 | desc->addr = priv->dma_hwdescs_phys |
1063 | + offsetof(struct ingenic_dma_hwdescs, palette); |
1064 | desc->cmd = JZ_LCD_CMD_ENABLE_PAL |
1065 | | (sizeof(priv->dma_hwdescs->palette) / 4); |
1066 | } |
1067 | |
1068 | static void ingenic_drm_configure_hwdesc_plane(struct ingenic_drm *priv, |
1069 | unsigned int plane) |
1070 | { |
1071 | ingenic_drm_configure_hwdesc(priv, hwdesc: plane, next_hwdesc: plane, id: 0xf0 | plane); |
1072 | } |
1073 | |
1074 | static void ingenic_drm_atomic_private_obj_fini(struct drm_device *drm, void *private_obj) |
1075 | { |
1076 | drm_atomic_private_obj_fini(obj: private_obj); |
1077 | } |
1078 | |
1079 | static int ingenic_drm_bind(struct device *dev, bool has_components) |
1080 | { |
1081 | struct platform_device *pdev = to_platform_device(dev); |
1082 | struct ingenic_drm_private_state *private_state; |
1083 | const struct jz_soc_info *soc_info; |
1084 | struct ingenic_drm *priv; |
1085 | struct clk *parent_clk; |
1086 | struct drm_plane *primary; |
1087 | struct drm_bridge *bridge; |
1088 | struct drm_panel *panel; |
1089 | struct drm_connector *connector; |
1090 | struct drm_encoder *encoder; |
1091 | struct ingenic_drm_bridge *ib; |
1092 | struct drm_device *drm; |
1093 | void __iomem *base; |
1094 | struct resource *res; |
1095 | struct regmap_config regmap_config; |
1096 | long parent_rate; |
1097 | unsigned int i, clone_mask = 0; |
1098 | int ret, irq; |
1099 | u32 osdc = 0; |
1100 | |
1101 | soc_info = of_device_get_match_data(dev); |
1102 | if (!soc_info) { |
1103 | dev_err(dev, "Missing platform data\n" ); |
1104 | return -EINVAL; |
1105 | } |
1106 | |
1107 | if (IS_ENABLED(CONFIG_OF_RESERVED_MEM)) { |
1108 | ret = of_reserved_mem_device_init(dev); |
1109 | |
1110 | if (ret && ret != -ENODEV) |
1111 | dev_warn(dev, "Failed to get reserved memory: %d\n" , ret); |
1112 | |
1113 | if (!ret) { |
1114 | ret = devm_add_action_or_reset(dev, ingenic_drm_release_rmem, dev); |
1115 | if (ret) |
1116 | return ret; |
1117 | } |
1118 | } |
1119 | |
1120 | priv = devm_drm_dev_alloc(dev, &ingenic_drm_driver_data, |
1121 | struct ingenic_drm, drm); |
1122 | if (IS_ERR(ptr: priv)) |
1123 | return PTR_ERR(ptr: priv); |
1124 | |
1125 | priv->soc_info = soc_info; |
1126 | priv->dev = dev; |
1127 | drm = &priv->drm; |
1128 | |
1129 | platform_set_drvdata(pdev, data: priv); |
1130 | |
1131 | ret = drmm_mode_config_init(dev: drm); |
1132 | if (ret) |
1133 | goto err_drvdata; |
1134 | |
1135 | drm->mode_config.min_width = 0; |
1136 | drm->mode_config.min_height = 0; |
1137 | drm->mode_config.max_width = soc_info->max_width; |
1138 | drm->mode_config.max_height = 4095; |
1139 | drm->mode_config.funcs = &ingenic_drm_mode_config_funcs; |
1140 | drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers; |
1141 | |
1142 | base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
1143 | if (IS_ERR(ptr: base)) { |
1144 | dev_err(dev, "Failed to get memory resource\n" ); |
1145 | ret = PTR_ERR(ptr: base); |
1146 | goto err_drvdata; |
1147 | } |
1148 | |
1149 | regmap_config = ingenic_drm_regmap_config; |
1150 | regmap_config.max_register = res->end - res->start; |
1151 | priv->map = devm_regmap_init_mmio(dev, base, |
1152 | ®map_config); |
1153 | if (IS_ERR(ptr: priv->map)) { |
1154 | dev_err(dev, "Failed to create regmap\n" ); |
1155 | ret = PTR_ERR(ptr: priv->map); |
1156 | goto err_drvdata; |
1157 | } |
1158 | |
1159 | irq = platform_get_irq(pdev, 0); |
1160 | if (irq < 0) { |
1161 | ret = irq; |
1162 | goto err_drvdata; |
1163 | } |
1164 | |
1165 | if (soc_info->needs_dev_clk) { |
1166 | priv->lcd_clk = devm_clk_get(dev, id: "lcd" ); |
1167 | if (IS_ERR(ptr: priv->lcd_clk)) { |
1168 | dev_err(dev, "Failed to get lcd clock\n" ); |
1169 | ret = PTR_ERR(ptr: priv->lcd_clk); |
1170 | goto err_drvdata; |
1171 | } |
1172 | } |
1173 | |
1174 | priv->pix_clk = devm_clk_get(dev, id: "lcd_pclk" ); |
1175 | if (IS_ERR(ptr: priv->pix_clk)) { |
1176 | dev_err(dev, "Failed to get pixel clock\n" ); |
1177 | ret = PTR_ERR(ptr: priv->pix_clk); |
1178 | goto err_drvdata; |
1179 | } |
1180 | |
1181 | priv->dma_hwdescs = dmam_alloc_coherent(dev, |
1182 | size: sizeof(*priv->dma_hwdescs), |
1183 | dma_handle: &priv->dma_hwdescs_phys, |
1184 | GFP_KERNEL); |
1185 | if (!priv->dma_hwdescs) { |
1186 | ret = -ENOMEM; |
1187 | goto err_drvdata; |
1188 | } |
1189 | |
1190 | /* Configure DMA hwdesc for foreground0 plane */ |
1191 | ingenic_drm_configure_hwdesc_plane(priv, plane: 0); |
1192 | |
1193 | /* Configure DMA hwdesc for foreground1 plane */ |
1194 | ingenic_drm_configure_hwdesc_plane(priv, plane: 1); |
1195 | |
1196 | /* Configure DMA hwdesc for palette */ |
1197 | ingenic_drm_configure_hwdesc_palette(priv); |
1198 | |
1199 | primary = priv->soc_info->has_osd ? &priv->f1 : &priv->f0; |
1200 | |
1201 | drm_plane_helper_add(plane: primary, funcs: &ingenic_drm_plane_helper_funcs); |
1202 | |
1203 | ret = drm_universal_plane_init(dev: drm, plane: primary, possible_crtcs: 1, |
1204 | funcs: &ingenic_drm_primary_plane_funcs, |
1205 | formats: priv->soc_info->formats_f1, |
1206 | format_count: priv->soc_info->num_formats_f1, |
1207 | NULL, type: DRM_PLANE_TYPE_PRIMARY, NULL); |
1208 | if (ret) { |
1209 | dev_err(dev, "Failed to register plane: %i\n" , ret); |
1210 | goto err_drvdata; |
1211 | } |
1212 | |
1213 | if (soc_info->map_noncoherent) |
1214 | drm_plane_enable_fb_damage_clips(plane: &priv->f1); |
1215 | |
1216 | drm_crtc_helper_add(crtc: &priv->crtc, funcs: &ingenic_drm_crtc_helper_funcs); |
1217 | |
1218 | ret = drm_crtc_init_with_planes(dev: drm, crtc: &priv->crtc, primary, |
1219 | NULL, funcs: &ingenic_drm_crtc_funcs, NULL); |
1220 | if (ret) { |
1221 | dev_err(dev, "Failed to init CRTC: %i\n" , ret); |
1222 | goto err_drvdata; |
1223 | } |
1224 | |
1225 | drm_crtc_enable_color_mgmt(crtc: &priv->crtc, degamma_lut_size: 0, has_ctm: false, |
1226 | ARRAY_SIZE(priv->dma_hwdescs->palette)); |
1227 | |
1228 | if (soc_info->has_osd) { |
1229 | drm_plane_helper_add(plane: &priv->f0, |
1230 | funcs: &ingenic_drm_plane_helper_funcs); |
1231 | |
1232 | ret = drm_universal_plane_init(dev: drm, plane: &priv->f0, possible_crtcs: 1, |
1233 | funcs: &ingenic_drm_primary_plane_funcs, |
1234 | formats: priv->soc_info->formats_f0, |
1235 | format_count: priv->soc_info->num_formats_f0, |
1236 | NULL, type: DRM_PLANE_TYPE_OVERLAY, |
1237 | NULL); |
1238 | if (ret) { |
1239 | dev_err(dev, "Failed to register overlay plane: %i\n" , |
1240 | ret); |
1241 | goto err_drvdata; |
1242 | } |
1243 | |
1244 | if (soc_info->map_noncoherent) |
1245 | drm_plane_enable_fb_damage_clips(plane: &priv->f0); |
1246 | |
1247 | if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && has_components) { |
1248 | ret = component_bind_all(parent: dev, data: drm); |
1249 | if (ret) { |
1250 | if (ret != -EPROBE_DEFER) |
1251 | dev_err(dev, "Failed to bind components: %i\n" , ret); |
1252 | goto err_drvdata; |
1253 | } |
1254 | |
1255 | ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv); |
1256 | if (ret) |
1257 | goto err_drvdata; |
1258 | |
1259 | priv->ipu_plane = drm_plane_from_index(dev: drm, idx: 2); |
1260 | if (!priv->ipu_plane) { |
1261 | dev_err(dev, "Failed to retrieve IPU plane\n" ); |
1262 | ret = -EINVAL; |
1263 | goto err_drvdata; |
1264 | } |
1265 | } |
1266 | } |
1267 | |
1268 | for (i = 0; ; i++) { |
1269 | ret = drm_of_find_panel_or_bridge(np: dev->of_node, port: 0, endpoint: i, panel: &panel, bridge: &bridge); |
1270 | if (ret) { |
1271 | if (ret == -ENODEV) |
1272 | break; /* we're done */ |
1273 | if (ret != -EPROBE_DEFER) |
1274 | dev_err(dev, "Failed to get bridge handle\n" ); |
1275 | goto err_drvdata; |
1276 | } |
1277 | |
1278 | if (panel) |
1279 | bridge = devm_drm_panel_bridge_add_typed(dev, panel, |
1280 | DRM_MODE_CONNECTOR_DPI); |
1281 | |
1282 | ib = drmm_encoder_alloc(drm, struct ingenic_drm_bridge, encoder, |
1283 | NULL, DRM_MODE_ENCODER_DPI, NULL); |
1284 | if (IS_ERR(ptr: ib)) { |
1285 | ret = PTR_ERR(ptr: ib); |
1286 | dev_err(dev, "Failed to init encoder: %d\n" , ret); |
1287 | goto err_drvdata; |
1288 | } |
1289 | |
1290 | encoder = &ib->encoder; |
1291 | encoder->possible_crtcs = drm_crtc_mask(crtc: &priv->crtc); |
1292 | |
1293 | drm_encoder_helper_add(encoder, funcs: &ingenic_drm_encoder_helper_funcs); |
1294 | |
1295 | ib->bridge.funcs = &ingenic_drm_bridge_funcs; |
1296 | ib->next_bridge = bridge; |
1297 | |
1298 | ret = drm_bridge_attach(encoder, bridge: &ib->bridge, NULL, |
1299 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
1300 | if (ret) { |
1301 | dev_err(dev, "Unable to attach bridge\n" ); |
1302 | goto err_drvdata; |
1303 | } |
1304 | |
1305 | connector = drm_bridge_connector_init(drm, encoder); |
1306 | if (IS_ERR(ptr: connector)) { |
1307 | dev_err(dev, "Unable to init connector\n" ); |
1308 | ret = PTR_ERR(ptr: connector); |
1309 | goto err_drvdata; |
1310 | } |
1311 | |
1312 | drm_connector_attach_encoder(connector, encoder); |
1313 | } |
1314 | |
1315 | drm_for_each_encoder(encoder, drm) { |
1316 | clone_mask |= BIT(drm_encoder_index(encoder)); |
1317 | } |
1318 | |
1319 | drm_for_each_encoder(encoder, drm) { |
1320 | encoder->possible_clones = clone_mask; |
1321 | } |
1322 | |
1323 | ret = devm_request_irq(dev, irq, handler: ingenic_drm_irq_handler, irqflags: 0, devname: drm->driver->name, dev_id: drm); |
1324 | if (ret) { |
1325 | dev_err(dev, "Unable to install IRQ handler\n" ); |
1326 | goto err_drvdata; |
1327 | } |
1328 | |
1329 | ret = drm_vblank_init(dev: drm, num_crtcs: 1); |
1330 | if (ret) { |
1331 | dev_err(dev, "Failed calling drm_vblank_init()\n" ); |
1332 | goto err_drvdata; |
1333 | } |
1334 | |
1335 | drm_mode_config_reset(dev: drm); |
1336 | |
1337 | ret = clk_prepare_enable(clk: priv->pix_clk); |
1338 | if (ret) { |
1339 | dev_err(dev, "Unable to start pixel clock\n" ); |
1340 | goto err_drvdata; |
1341 | } |
1342 | |
1343 | if (priv->lcd_clk) { |
1344 | parent_clk = clk_get_parent(clk: priv->lcd_clk); |
1345 | parent_rate = clk_get_rate(clk: parent_clk); |
1346 | |
1347 | /* LCD Device clock must be 3x the pixel clock for STN panels, |
1348 | * or 1.5x the pixel clock for TFT panels. To avoid having to |
1349 | * check for the LCD device clock everytime we do a mode change, |
1350 | * we set the LCD device clock to the highest rate possible. |
1351 | */ |
1352 | ret = clk_set_rate(clk: priv->lcd_clk, rate: parent_rate); |
1353 | if (ret) { |
1354 | dev_err(dev, "Unable to set LCD clock rate\n" ); |
1355 | goto err_pixclk_disable; |
1356 | } |
1357 | |
1358 | ret = clk_prepare_enable(clk: priv->lcd_clk); |
1359 | if (ret) { |
1360 | dev_err(dev, "Unable to start lcd clock\n" ); |
1361 | goto err_pixclk_disable; |
1362 | } |
1363 | } |
1364 | |
1365 | /* Enable OSD if available */ |
1366 | if (soc_info->has_osd) |
1367 | osdc |= JZ_LCD_OSDC_OSDEN; |
1368 | if (soc_info->has_alpha) |
1369 | osdc |= JZ_LCD_OSDC_ALPHAEN; |
1370 | regmap_write(map: priv->map, JZ_REG_LCD_OSDC, val: osdc); |
1371 | |
1372 | mutex_init(&priv->clk_mutex); |
1373 | priv->clock_nb.notifier_call = ingenic_drm_update_pixclk; |
1374 | |
1375 | parent_clk = clk_get_parent(clk: priv->pix_clk); |
1376 | ret = clk_notifier_register(clk: parent_clk, nb: &priv->clock_nb); |
1377 | if (ret) { |
1378 | dev_err(dev, "Unable to register clock notifier\n" ); |
1379 | goto err_devclk_disable; |
1380 | } |
1381 | |
1382 | private_state = kzalloc(size: sizeof(*private_state), GFP_KERNEL); |
1383 | if (!private_state) { |
1384 | ret = -ENOMEM; |
1385 | goto err_clk_notifier_unregister; |
1386 | } |
1387 | |
1388 | drm_atomic_private_obj_init(dev: drm, obj: &priv->private_obj, state: &private_state->base, |
1389 | funcs: &ingenic_drm_private_state_funcs); |
1390 | |
1391 | ret = drmm_add_action_or_reset(drm, ingenic_drm_atomic_private_obj_fini, |
1392 | &priv->private_obj); |
1393 | if (ret) |
1394 | goto err_private_state_free; |
1395 | |
1396 | ret = drm_dev_register(dev: drm, flags: 0); |
1397 | if (ret) { |
1398 | dev_err(dev, "Failed to register DRM driver\n" ); |
1399 | goto err_clk_notifier_unregister; |
1400 | } |
1401 | |
1402 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 32); |
1403 | |
1404 | return 0; |
1405 | |
1406 | err_private_state_free: |
1407 | kfree(objp: private_state); |
1408 | err_clk_notifier_unregister: |
1409 | clk_notifier_unregister(clk: parent_clk, nb: &priv->clock_nb); |
1410 | err_devclk_disable: |
1411 | if (priv->lcd_clk) |
1412 | clk_disable_unprepare(clk: priv->lcd_clk); |
1413 | err_pixclk_disable: |
1414 | clk_disable_unprepare(clk: priv->pix_clk); |
1415 | err_drvdata: |
1416 | platform_set_drvdata(pdev, NULL); |
1417 | return ret; |
1418 | } |
1419 | |
1420 | static int ingenic_drm_bind_with_components(struct device *dev) |
1421 | { |
1422 | return ingenic_drm_bind(dev, has_components: true); |
1423 | } |
1424 | |
1425 | static void ingenic_drm_unbind(struct device *dev) |
1426 | { |
1427 | struct ingenic_drm *priv = dev_get_drvdata(dev); |
1428 | struct clk *parent_clk = clk_get_parent(clk: priv->pix_clk); |
1429 | |
1430 | clk_notifier_unregister(clk: parent_clk, nb: &priv->clock_nb); |
1431 | if (priv->lcd_clk) |
1432 | clk_disable_unprepare(clk: priv->lcd_clk); |
1433 | clk_disable_unprepare(clk: priv->pix_clk); |
1434 | |
1435 | drm_dev_unregister(dev: &priv->drm); |
1436 | drm_atomic_helper_shutdown(dev: &priv->drm); |
1437 | dev_set_drvdata(dev, NULL); |
1438 | } |
1439 | |
1440 | static const struct component_master_ops ingenic_master_ops = { |
1441 | .bind = ingenic_drm_bind_with_components, |
1442 | .unbind = ingenic_drm_unbind, |
1443 | }; |
1444 | |
1445 | static int ingenic_drm_probe(struct platform_device *pdev) |
1446 | { |
1447 | struct device *dev = &pdev->dev; |
1448 | struct component_match *match = NULL; |
1449 | struct device_node *np; |
1450 | |
1451 | if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) |
1452 | return ingenic_drm_bind(dev, has_components: false); |
1453 | |
1454 | /* IPU is at port address 8 */ |
1455 | np = of_graph_get_remote_node(node: dev->of_node, port: 8, endpoint: 0); |
1456 | if (!np) |
1457 | return ingenic_drm_bind(dev, has_components: false); |
1458 | |
1459 | drm_of_component_match_add(master: dev, matchptr: &match, compare: component_compare_of, node: np); |
1460 | of_node_put(node: np); |
1461 | |
1462 | return component_master_add_with_match(dev, &ingenic_master_ops, match); |
1463 | } |
1464 | |
1465 | static void ingenic_drm_remove(struct platform_device *pdev) |
1466 | { |
1467 | struct device *dev = &pdev->dev; |
1468 | |
1469 | if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) |
1470 | ingenic_drm_unbind(dev); |
1471 | else |
1472 | component_master_del(dev, &ingenic_master_ops); |
1473 | } |
1474 | |
1475 | static void ingenic_drm_shutdown(struct platform_device *pdev) |
1476 | { |
1477 | struct ingenic_drm *priv = platform_get_drvdata(pdev); |
1478 | |
1479 | if (priv) |
1480 | drm_atomic_helper_shutdown(dev: &priv->drm); |
1481 | } |
1482 | |
1483 | static int ingenic_drm_suspend(struct device *dev) |
1484 | { |
1485 | struct ingenic_drm *priv = dev_get_drvdata(dev); |
1486 | |
1487 | return drm_mode_config_helper_suspend(dev: &priv->drm); |
1488 | } |
1489 | |
1490 | static int ingenic_drm_resume(struct device *dev) |
1491 | { |
1492 | struct ingenic_drm *priv = dev_get_drvdata(dev); |
1493 | |
1494 | return drm_mode_config_helper_resume(dev: &priv->drm); |
1495 | } |
1496 | |
1497 | static DEFINE_SIMPLE_DEV_PM_OPS(ingenic_drm_pm_ops, |
1498 | ingenic_drm_suspend, ingenic_drm_resume); |
1499 | |
1500 | static const u32 jz4740_formats[] = { |
1501 | DRM_FORMAT_XRGB1555, |
1502 | DRM_FORMAT_RGB565, |
1503 | DRM_FORMAT_XRGB8888, |
1504 | }; |
1505 | |
1506 | static const u32 jz4725b_formats_f1[] = { |
1507 | DRM_FORMAT_XRGB1555, |
1508 | DRM_FORMAT_RGB565, |
1509 | DRM_FORMAT_XRGB8888, |
1510 | }; |
1511 | |
1512 | static const u32 jz4725b_formats_f0[] = { |
1513 | DRM_FORMAT_C8, |
1514 | DRM_FORMAT_XRGB1555, |
1515 | DRM_FORMAT_RGB565, |
1516 | DRM_FORMAT_XRGB8888, |
1517 | }; |
1518 | |
1519 | static const u32 jz4770_formats_f1[] = { |
1520 | DRM_FORMAT_XRGB1555, |
1521 | DRM_FORMAT_RGB565, |
1522 | DRM_FORMAT_RGB888, |
1523 | DRM_FORMAT_XRGB8888, |
1524 | DRM_FORMAT_XRGB2101010, |
1525 | }; |
1526 | |
1527 | static const u32 jz4770_formats_f0[] = { |
1528 | DRM_FORMAT_C8, |
1529 | DRM_FORMAT_XRGB1555, |
1530 | DRM_FORMAT_RGB565, |
1531 | DRM_FORMAT_RGB888, |
1532 | DRM_FORMAT_XRGB8888, |
1533 | DRM_FORMAT_XRGB2101010, |
1534 | }; |
1535 | |
1536 | static const struct jz_soc_info jz4740_soc_info = { |
1537 | .needs_dev_clk = true, |
1538 | .has_osd = false, |
1539 | .map_noncoherent = false, |
1540 | .max_width = 800, |
1541 | .max_height = 600, |
1542 | .max_burst = JZ_LCD_CTRL_BURST_16, |
1543 | .formats_f1 = jz4740_formats, |
1544 | .num_formats_f1 = ARRAY_SIZE(jz4740_formats), |
1545 | /* JZ4740 has only one plane */ |
1546 | }; |
1547 | |
1548 | static const struct jz_soc_info jz4725b_soc_info = { |
1549 | .needs_dev_clk = false, |
1550 | .has_osd = true, |
1551 | .map_noncoherent = false, |
1552 | .max_width = 800, |
1553 | .max_height = 600, |
1554 | .max_burst = JZ_LCD_CTRL_BURST_16, |
1555 | .formats_f1 = jz4725b_formats_f1, |
1556 | .num_formats_f1 = ARRAY_SIZE(jz4725b_formats_f1), |
1557 | .formats_f0 = jz4725b_formats_f0, |
1558 | .num_formats_f0 = ARRAY_SIZE(jz4725b_formats_f0), |
1559 | }; |
1560 | |
1561 | static const struct jz_soc_info jz4760_soc_info = { |
1562 | .needs_dev_clk = false, |
1563 | .has_osd = true, |
1564 | .map_noncoherent = false, |
1565 | .max_width = 1280, |
1566 | .max_height = 720, |
1567 | .max_burst = JZ_LCD_CTRL_BURST_32, |
1568 | .formats_f1 = jz4770_formats_f1, |
1569 | .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), |
1570 | .formats_f0 = jz4770_formats_f0, |
1571 | .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), |
1572 | }; |
1573 | |
1574 | static const struct jz_soc_info jz4760b_soc_info = { |
1575 | .needs_dev_clk = false, |
1576 | .has_osd = true, |
1577 | .map_noncoherent = false, |
1578 | .max_width = 1280, |
1579 | .max_height = 720, |
1580 | .max_burst = JZ_LCD_CTRL_BURST_64, |
1581 | .formats_f1 = jz4770_formats_f1, |
1582 | .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), |
1583 | .formats_f0 = jz4770_formats_f0, |
1584 | .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), |
1585 | }; |
1586 | |
1587 | static const struct jz_soc_info jz4770_soc_info = { |
1588 | .needs_dev_clk = false, |
1589 | .has_osd = true, |
1590 | .map_noncoherent = true, |
1591 | .max_width = 1280, |
1592 | .max_height = 720, |
1593 | .max_burst = JZ_LCD_CTRL_BURST_64, |
1594 | .formats_f1 = jz4770_formats_f1, |
1595 | .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), |
1596 | .formats_f0 = jz4770_formats_f0, |
1597 | .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), |
1598 | }; |
1599 | |
1600 | static const struct jz_soc_info jz4780_soc_info = { |
1601 | .needs_dev_clk = true, |
1602 | .has_osd = true, |
1603 | .has_alpha = true, |
1604 | .use_extended_hwdesc = true, |
1605 | .plane_f0_not_working = true, /* REVISIT */ |
1606 | .max_width = 4096, |
1607 | .max_height = 2048, |
1608 | .max_burst = JZ_LCD_CTRL_BURST_64, |
1609 | .formats_f1 = jz4770_formats_f1, |
1610 | .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), |
1611 | .formats_f0 = jz4770_formats_f0, |
1612 | .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), |
1613 | }; |
1614 | |
1615 | static const struct of_device_id ingenic_drm_of_match[] = { |
1616 | { .compatible = "ingenic,jz4740-lcd" , .data = &jz4740_soc_info }, |
1617 | { .compatible = "ingenic,jz4725b-lcd" , .data = &jz4725b_soc_info }, |
1618 | { .compatible = "ingenic,jz4760-lcd" , .data = &jz4760_soc_info }, |
1619 | { .compatible = "ingenic,jz4760b-lcd" , .data = &jz4760b_soc_info }, |
1620 | { .compatible = "ingenic,jz4770-lcd" , .data = &jz4770_soc_info }, |
1621 | { .compatible = "ingenic,jz4780-lcd" , .data = &jz4780_soc_info }, |
1622 | { /* sentinel */ }, |
1623 | }; |
1624 | MODULE_DEVICE_TABLE(of, ingenic_drm_of_match); |
1625 | |
1626 | static struct platform_driver ingenic_drm_driver = { |
1627 | .driver = { |
1628 | .name = "ingenic-drm" , |
1629 | .pm = pm_sleep_ptr(&ingenic_drm_pm_ops), |
1630 | .of_match_table = of_match_ptr(ingenic_drm_of_match), |
1631 | }, |
1632 | .probe = ingenic_drm_probe, |
1633 | .remove_new = ingenic_drm_remove, |
1634 | .shutdown = ingenic_drm_shutdown, |
1635 | }; |
1636 | |
1637 | static int ingenic_drm_init(void) |
1638 | { |
1639 | int err; |
1640 | |
1641 | if (drm_firmware_drivers_only()) |
1642 | return -ENODEV; |
1643 | |
1644 | if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { |
1645 | err = platform_driver_register(ingenic_ipu_driver_ptr); |
1646 | if (err) |
1647 | return err; |
1648 | } |
1649 | |
1650 | err = platform_driver_register(&ingenic_drm_driver); |
1651 | if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && err) |
1652 | platform_driver_unregister(ingenic_ipu_driver_ptr); |
1653 | |
1654 | return err; |
1655 | } |
1656 | module_init(ingenic_drm_init); |
1657 | |
1658 | static void ingenic_drm_exit(void) |
1659 | { |
1660 | platform_driver_unregister(&ingenic_drm_driver); |
1661 | |
1662 | if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) |
1663 | platform_driver_unregister(ingenic_ipu_driver_ptr); |
1664 | } |
1665 | module_exit(ingenic_drm_exit); |
1666 | |
1667 | MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>" ); |
1668 | MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n" ); |
1669 | MODULE_LICENSE("GPL" ); |
1670 | |