1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <drm/drmP.h>
21#include <drm/drm_crtc.h>
22#include <drm/drm_crtc_helper.h>
23#include <drm/drm_gem_cma_helper.h>
24
25#include <linux/clk.h>
26#include <linux/delay.h>
27#include <linux/device.h>
28
29#include <video/videomode.h>
30
31#include "xylon_crtc.h"
32#include "xylon_drv.h"
33#include "xylon_logicvc_helper.h"
34#include "xylon_logicvc_hw.h"
35#include "xylon_plane.h"
36#include "xylon_property.h"
37
38struct xylon_drm_crtc_properties {
39 struct drm_property *bg_color;
40 struct drm_property *layer_update;
41 bool layer_update_initval;
42 struct drm_property *pixel_data_polarity;
43 bool pixel_data_polarity_initval;
44 struct drm_property *pixel_data_trigger;
45 bool pixel_data_trigger_initval;
46};
47
48struct xylon_drm_crtc {
49 struct drm_crtc base;
50 struct drm_pending_vblank_event *event;
51 struct xylon_drm_crtc_properties properties;
52 struct xylon_cvc *cvc;
53 struct xylon_drm_plane_manager *manager;
54 struct clk *pixel_clock;
55 struct xylon_cvc_fix fix;
56 struct videomode vmode;
57 u32 primary_id;
58 int dpms;
59};
60
61#define to_xylon_crtc(x) container_of(x, struct xylon_drm_crtc, base)
62
63static int xylon_drm_crtc_clk_set(struct xylon_drm_crtc *crtc)
64{
65 int ret;
66
67 ret = clk_set_rate(crtc->pixel_clock, crtc->vmode.pixelclock);
68 if (ret) {
69 DRM_ERROR("failed set pixel clock\n");
70 return ret;
71 }
72 DRM_DEBUG("pixel clock %ld -> %ld\n", crtc->vmode.pixelclock,
73 clk_get_rate(crtc->pixel_clock));
74
75 return 0;
76}
77
78static void xylon_drm_crtc_dpms(struct drm_crtc *base_crtc, int dpms)
79{
80 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
81
82 if (crtc->dpms == dpms)
83 return;
84
85 crtc->dpms = dpms;
86
87 switch (dpms) {
88 case DRM_MODE_DPMS_ON:
89 case DRM_MODE_DPMS_STANDBY:
90 xylon_drm_plane_dpms(base_crtc->primary, dpms);
91 break;
92 default:
93 xylon_cvc_disable(crtc->cvc);
94 break;
95 }
96}
97
98static void xylon_drm_crtc_prepare(struct drm_crtc *base_crtc)
99{
100 xylon_drm_crtc_dpms(base_crtc, DRM_MODE_DPMS_STANDBY);
101}
102
103static void xylon_drm_crtc_commit(struct drm_crtc *base_crtc)
104{
105 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
106
107 xylon_drm_crtc_clk_set(crtc);
108
109 xylon_drm_plane_commit(base_crtc->primary);
110
111 xylon_cvc_enable(crtc->cvc, &crtc->vmode);
112
113 xylon_drm_crtc_dpms(base_crtc, DRM_MODE_DPMS_ON);
114}
115
116static bool xylon_drm_crtc_mode_fixup(struct drm_crtc *base_crtc,
117 const struct drm_display_mode *mode,
118 struct drm_display_mode *adjusted_mode)
119{
120 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
121
122 if ((mode->hdisplay >= crtc->fix.hres_min &&
123 mode->hdisplay <= crtc->fix.hres_max) &&
124 (mode->vdisplay >= crtc->fix.vres_min &&
125 mode->vdisplay <= crtc->fix.vres_max))
126 return true;
127
128 return false;
129}
130
131static int xylon_drm_crtc_mode_set(struct drm_crtc *base_crtc,
132 struct drm_display_mode *mode,
133 struct drm_display_mode *adjusted_mode,
134 int x, int y,
135 struct drm_framebuffer *old_fb)
136{
137 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
138 struct drm_display_mode *dm = adjusted_mode;
139 int ret;
140
141 crtc->vmode.pixelclock = dm->clock * KHZ;
142 crtc->vmode.hactive = dm->hdisplay;
143 crtc->vmode.hfront_porch = dm->hsync_start - dm->hdisplay;
144 crtc->vmode.hback_porch = dm->htotal - dm->hsync_end;
145 crtc->vmode.hsync_len = dm->hsync_end - dm->hsync_start;
146 crtc->vmode.vactive = dm->vdisplay;
147 crtc->vmode.vfront_porch = dm->vsync_start - dm->vdisplay;
148 crtc->vmode.vback_porch = dm->vtotal - dm->vsync_end;
149 crtc->vmode.vsync_len = dm->vsync_end - dm->vsync_start;
150
151 ret = xylon_drm_plane_fb_set(base_crtc->primary, base_crtc->primary->fb,
152 0, 0, dm->hdisplay, dm->vdisplay,
153 x, y, dm->hdisplay, dm->vdisplay);
154 if (ret) {
155 DRM_ERROR("failed set plane mode\n");
156 return ret;
157 }
158
159 return 0;
160}
161
162static int xylon_drm_crtc_mode_set_base(struct drm_crtc *base_crtc,
163 int x, int y,
164 struct drm_framebuffer *old_fb)
165{
166 const struct drm_plane_funcs *funcs = base_crtc->primary->funcs;
167 int ret;
168
169 ret = funcs->update_plane(base_crtc->primary,
170 base_crtc,
171 base_crtc->primary->fb,
172 0, 0,
173 base_crtc->hwmode.hdisplay,
174 base_crtc->hwmode.vdisplay,
175 x << 16, y << 16,
176 base_crtc->hwmode.hdisplay << 16,
177 base_crtc->hwmode.vdisplay << 16);
178 if (ret) {
179 DRM_ERROR("failed set plane mode\n");
180 return ret;
181 }
182
183 return 0;
184}
185
186static void xylon_drm_crtc_load_lut(struct drm_crtc *base_crtc)
187{
188}
189
190static struct drm_crtc_helper_funcs xylon_drm_crtc_helper_funcs = {
191 .dpms = xylon_drm_crtc_dpms,
192 .prepare = xylon_drm_crtc_prepare,
193 .commit = xylon_drm_crtc_commit,
194 .mode_fixup = xylon_drm_crtc_mode_fixup,
195 .mode_set = xylon_drm_crtc_mode_set,
196 .mode_set_base = xylon_drm_crtc_mode_set_base,
197 .load_lut = xylon_drm_crtc_load_lut,
198};
199
200static void xylon_drm_crtc_destroy(struct drm_crtc *base_crtc)
201{
202 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
203
204 xylon_drm_crtc_dpms(base_crtc, DRM_MODE_DPMS_OFF);
205
206 drm_crtc_cleanup(base_crtc);
207
208 clk_disable_unprepare(crtc->pixel_clock);
209}
210
211void xylon_drm_crtc_cancel_page_flip(struct drm_crtc *base_crtc,
212 struct drm_file *file)
213{
214 struct drm_device *dev = base_crtc->dev;
215 struct drm_pending_vblank_event *event;
216 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
217 unsigned long flags;
218
219 spin_lock_irqsave(&dev->event_lock, flags);
220 event = crtc->event;
221 if (event && (event->base.file_priv == file)) {
222 crtc->event = NULL;
223 event->base.destroy(&event->base);
224 drm_vblank_put(dev, 0);
225 }
226 spin_unlock_irqrestore(&dev->event_lock, flags);
227}
228
229static int xylon_drm_crtc_page_flip(struct drm_crtc *base_crtc,
230 struct drm_framebuffer *fb,
231 struct drm_pending_vblank_event *event,
232 u32 page_flip_flags)
233{
234 struct drm_device *dev = base_crtc->dev;
235 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
236 unsigned long flags;
237 int ret;
238
239 spin_lock_irqsave(&dev->event_lock, flags);
240 if (crtc->event != NULL) {
241 spin_unlock_irqrestore(&dev->event_lock, flags);
242 return -EBUSY;
243 }
244 spin_unlock_irqrestore(&dev->event_lock, flags);
245
246 ret = xylon_drm_plane_fb_set(base_crtc->primary, fb,
247 0, 0,
248 base_crtc->hwmode.hdisplay,
249 base_crtc->hwmode.vdisplay,
250 base_crtc->x, base_crtc->y,
251 base_crtc->hwmode.hdisplay,
252 base_crtc->hwmode.vdisplay);
253 if (ret) {
254 DRM_ERROR("failed mode set plane\n");
255 return ret;
256 }
257
258 xylon_drm_plane_commit(base_crtc->primary);
259
260 base_crtc->primary->fb = fb;
261
262 if (event) {
263 event->pipe = 0;
264 drm_vblank_get(dev, 0);
265 spin_lock_irqsave(&dev->event_lock, flags);
266 crtc->event = event;
267 spin_unlock_irqrestore(&dev->event_lock, flags);
268 }
269
270 return 0;
271}
272
273static int xylon_drm_crtc_set_property(struct drm_crtc *base_crtc,
274 struct drm_property *property,
275 u64 value)
276{
277 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
278 struct xylon_drm_crtc_properties *props = &crtc->properties;
279 struct xylon_drm_plane_op op;
280 u32 val = (u32)value;
281 s64 x = -1;
282 s64 y = -1;
283
284 if (property == props->bg_color) {
285 op.id = XYLON_DRM_PLANE_OP_ID_BACKGROUND_COLOR;
286 op.param = val;
287 } else if (property == props->layer_update) {
288 xylon_cvc_ctrl(crtc->cvc, LOGICVC_LAYER_UPDATE,
289 (bool)val);
290 } else if (property == props->pixel_data_polarity) {
291 xylon_cvc_ctrl(crtc->cvc, LOGICVC_PIXEL_DATA_INVERT,
292 (bool)val);
293 } else if (property == props->pixel_data_trigger) {
294 xylon_cvc_ctrl(crtc->cvc, LOGICVC_PIXEL_DATA_TRIGGER_INVERT,
295 (bool)val);
296 } else {
297 return -EINVAL;
298 }
299
300 if (x > -1 && y > -1) {
301 if (xylon_drm_plane_fb_set(base_crtc->primary,
302 base_crtc->primary->fb,
303 (u32)x, (u32)y,
304 base_crtc->hwmode.hdisplay - x,
305 base_crtc->hwmode.vdisplay - y,
306 base_crtc->x, base_crtc->y,
307 base_crtc->hwmode.hdisplay - x,
308 base_crtc->hwmode.vdisplay - y))
309 DRM_ERROR("failed set position\n");
310 else
311 xylon_drm_plane_commit(base_crtc->primary);
312 } else {
313 xylon_drm_plane_op(base_crtc->primary, &op);
314 }
315
316 return 0;
317}
318
319static struct drm_crtc_funcs xylon_drm_crtc_funcs = {
320 .destroy = xylon_drm_crtc_destroy,
321 .set_config = drm_crtc_helper_set_config,
322 .page_flip = xylon_drm_crtc_page_flip,
323 .set_property = xylon_drm_crtc_set_property,
324};
325
326static void xylon_drm_crtc_vblank_handler(struct drm_crtc *base_crtc)
327{
328 struct drm_device *dev = base_crtc->dev;
329 struct drm_pending_vblank_event *event;
330 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
331 unsigned long flags;
332
333 drm_handle_vblank(dev, 0);
334
335 spin_lock_irqsave(&dev->event_lock, flags);
336 event = crtc->event;
337 crtc->event = NULL;
338 if (event) {
339 drm_send_vblank_event(dev, 0, event);
340 drm_vblank_put(dev, 0);
341 }
342 spin_unlock_irqrestore(&dev->event_lock, flags);
343}
344
345void xylon_drm_crtc_vblank(struct drm_crtc *base_crtc, bool enabled)
346{
347 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
348
349 xylon_cvc_int_state(crtc->cvc, LOGICVC_INT_V_SYNC, enabled);
350}
351
352void xylon_drm_crtc_int_handle(struct drm_crtc *base_crtc)
353{
354 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
355 u32 active = xylon_cvc_int_get_active(crtc->cvc);
356 u32 handled = 0;
357
358 if (active & LOGICVC_INT_V_SYNC) {
359 xylon_drm_crtc_vblank_handler(base_crtc);
360 handled |= LOGICVC_INT_V_SYNC;
361 }
362
363 xylon_cvc_int_clear_active(crtc->cvc, handled);
364}
365
366void xylon_drm_crtc_int_hw_enable(struct drm_crtc *base_crtc)
367{
368 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
369
370 xylon_cvc_int_hw_enable(crtc->cvc);
371}
372
373void xylon_drm_crtc_int_hw_disable(struct drm_crtc *base_crtc)
374{
375 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
376
377 xylon_cvc_int_hw_disable(crtc->cvc);
378}
379
380int xylon_drm_crtc_int_request(struct drm_crtc *base_crtc, unsigned long flags,
381 irq_handler_t handler, void *dev)
382{
383 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
384
385 return xylon_cvc_int_request(crtc->cvc, flags, handler, dev);
386}
387
388void xylon_drm_crtc_int_free(struct drm_crtc *base_crtc, void *dev)
389{
390 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
391
392 xylon_cvc_int_free(crtc->cvc, dev);
393}
394
395bool xylon_drm_crtc_check_format(struct drm_crtc *base_crtc, u32 fourcc)
396{
397 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
398
399 return xylon_drm_plane_check_format(crtc->manager, fourcc);
400}
401
402void xylon_drm_crtc_get_fix_parameters(struct drm_crtc *base_crtc)
403{
404 struct drm_device *dev = base_crtc->dev;
405 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
406
407 xylon_cvc_get_fix_parameters(crtc->cvc, &crtc->fix);
408
409 dev->mode_config.min_width = crtc->fix.x_min;
410 dev->mode_config.min_height = crtc->fix.y_min;
411 dev->mode_config.max_width = crtc->fix.x_max;
412 dev->mode_config.max_height = crtc->fix.y_max;
413}
414
415int xylon_drm_crtc_get_param(struct drm_crtc *base_crtc, unsigned int *p,
416 enum xylon_drm_crtc_buff param)
417{
418 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
419
420 if (crtc->fix.x_max == 0)
421 return -ENODEV;
422
423 switch (param) {
424 case XYLON_DRM_CRTC_BUFF_BPP:
425 *p = xylon_drm_plane_get_bits_per_pixel(base_crtc->primary);
426 break;
427 case XYLON_DRM_CRTC_BUFF_WIDTH:
428 *p = crtc->fix.x_max;
429 break;
430 case XYLON_DRM_CRTC_BUFF_HEIGHT:
431 *p = crtc->fix.y_max;
432 break;
433 }
434
435 return 0;
436}
437
438static int xylon_drm_crtc_create_properties(struct drm_crtc *base_crtc)
439{
440 struct drm_device *dev = base_crtc->dev;
441 struct drm_mode_object *obj = &base_crtc->base;
442 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
443 struct xylon_drm_crtc_properties *props = &crtc->properties;
444 bool bg_prop = xylon_cvc_get_info(crtc->cvc,
445 LOGICVC_INFO_BACKGROUND_LAYER,
446 0);
447 int size;
448
449 size = xylon_drm_property_size(property_layer_update);
450 if (xylon_drm_property_create_list(dev, obj,
451 &props->layer_update,
452 property_layer_update,
453 "layer_update",
454 size))
455 return -EINVAL;
456 size = xylon_drm_property_size(property_pixel_data_polarity);
457 if (xylon_drm_property_create_list(dev, obj,
458 &props->pixel_data_polarity,
459 property_pixel_data_polarity,
460 "pixel_data_polarity",
461 size))
462 return -EINVAL;
463 size = xylon_drm_property_size(property_pixel_data_trigger);
464 if (xylon_drm_property_create_list(dev, obj,
465 &props->pixel_data_trigger,
466 property_pixel_data_trigger,
467 "pixel_data_trigger",
468 size))
469 return -EINVAL;
470 if (bg_prop &&
471 xylon_drm_property_create_range(dev, obj,
472 &props->bg_color,
473 "background_color",
474 XYLON_DRM_PROPERTY_COLOR_MIN,
475 XYLON_DRM_PROPERTY_COLOR_MAX,
476 XYLON_DRM_PROPERTY_COLOR_MIN))
477 return -EINVAL;
478
479 return 0;
480}
481
482static void xylon_drm_crtc_properties_initial_value(struct drm_crtc *base_crtc)
483{
484 struct drm_mode_object *obj = &base_crtc->base;
485 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
486 struct xylon_drm_crtc_properties *props = &crtc->properties;
487 bool *val;
488
489 val = &props->layer_update_initval;
490 *val = xylon_cvc_get_info(crtc->cvc, LOGICVC_INFO_LAYER_UPDATE, 0);
491 drm_object_property_set_value(obj, props->layer_update, *val);
492
493 val = &props->pixel_data_polarity_initval;
494 *val = xylon_cvc_get_info(crtc->cvc, LOGICVC_INFO_PIXEL_DATA_INVERT, 0);
495 drm_object_property_set_value(obj, props->pixel_data_polarity, *val);
496
497 val = &props->pixel_data_trigger_initval;
498 *val = xylon_cvc_get_info(crtc->cvc,
499 LOGICVC_INFO_PIXEL_DATA_TRIGGER_INVERT, 0);
500 drm_object_property_set_value(obj, props->pixel_data_trigger, *val);
501}
502
503struct drm_crtc *xylon_drm_crtc_create(struct drm_device *dev)
504{
505 struct device_node *sub_node;
506 struct drm_plane *primary;
507 struct xylon_drm_crtc *crtc;
508 int ret;
509
510 sub_node = of_parse_phandle(dev->dev->of_node, "device", 0);
511 if (!sub_node) {
512 DRM_ERROR("failed get logicvc\n");
513 return ERR_PTR(-ENODEV);
514 }
515
516 crtc = devm_kzalloc(dev->dev, sizeof(*crtc), GFP_KERNEL);
517 if (!crtc)
518 return ERR_PTR(-ENOMEM);
519
520 crtc->cvc = xylon_cvc_probe(dev->dev, sub_node);
521 of_node_put(sub_node);
522 if (IS_ERR(crtc->cvc)) {
523 DRM_ERROR("failed probe logicvc\n");
524 return ERR_CAST(crtc->cvc);
525 }
526
527 crtc->manager = xylon_drm_plane_probe_manager(dev, crtc->cvc);
528 if (IS_ERR(crtc->manager)) {
529 DRM_ERROR("failed probe plane manager\n");
530 return ERR_CAST(crtc->manager);
531 }
532
533 ret = of_property_read_u32(dev->dev->of_node, "primary-plane",
534 &crtc->primary_id);
535 if (ret)
536 DRM_INFO("no private-plane property\n");
537
538 ret = xylon_drm_plane_create_all(crtc->manager, 1, crtc->primary_id);
539 if (ret) {
540 DRM_ERROR("failed create planes\n");
541 goto err_out;
542 }
543
544 crtc->pixel_clock = devm_clk_get(dev->dev, NULL);
545 if (IS_ERR(crtc->pixel_clock)) {
546 DRM_ERROR("failed get pixel clock\n");
547 ret = -EPROBE_DEFER;
548 goto err_out;
549 }
550
551 ret = clk_prepare_enable(crtc->pixel_clock);
552 if (ret) {
553 DRM_ERROR("failed prepare/enable clock\n");
554 goto err_out;
555 }
556
557 primary = xylon_drm_plane_get_base(crtc->manager, crtc->primary_id);
558 ret = drm_crtc_init_with_planes(dev, &crtc->base, primary, NULL,
559 &xylon_drm_crtc_funcs, NULL);
560 if (ret) {
561 DRM_ERROR("failed initialize crtc\n");
562 goto err_out;
563 }
564 drm_crtc_helper_add(&crtc->base, &xylon_drm_crtc_helper_funcs);
565
566 ret = xylon_drm_crtc_create_properties(&crtc->base);
567 if (ret) {
568 DRM_ERROR("failed initialize crtc properties\n");
569 goto err_out;
570 }
571
572 xylon_drm_crtc_properties_initial_value(&crtc->base);
573
574 return &crtc->base;
575
576err_out:
577 return ERR_PTR(ret);
578}
579
580void xylon_drm_crtc_properties_restore(struct drm_crtc *base_crtc)
581{
582 struct drm_mode_object *obj = &base_crtc->base;
583 struct xylon_drm_crtc *crtc = to_xylon_crtc(base_crtc);
584 struct xylon_drm_crtc_properties *props = &crtc->properties;
585
586 xylon_drm_crtc_set_property(base_crtc, props->layer_update,
587 props->layer_update_initval);
588 drm_object_property_set_value(obj, props->layer_update,
589 props->layer_update_initval);
590 xylon_drm_crtc_set_property(base_crtc, props->pixel_data_polarity,
591 props->pixel_data_polarity_initval);
592 drm_object_property_set_value(obj, props->pixel_data_polarity,
593 props->pixel_data_polarity_initval);
594 xylon_drm_crtc_set_property(base_crtc, props->pixel_data_trigger,
595 props->pixel_data_trigger_initval);
596 drm_object_property_set_value(obj, props->pixel_data_trigger,
597 props->pixel_data_trigger_initval);
598
599 xylon_drm_plane_properties_restore(crtc->manager);
600}
601