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_gem_cma_helper.h>
23
24#include <linux/device.h>
25#include <linux/platform_device.h>
26
27#include "xylon_drv.h"
28#include "xylon_fb.h"
29#include "xylon_logicvc_helper.h"
30#include "xylon_logicvc_layer.h"
31#include "xylon_plane.h"
32#include "xylon_property.h"
33
34struct xylon_drm_plane_properties {
35 struct drm_property *color_transparency;
36 struct drm_property *interlace;
37 struct drm_property *transparency;
38 struct drm_property *transparent_color;
39};
40
41struct xylon_drm_plane {
42 struct drm_plane base;
43 struct xylon_drm_plane_manager *manager;
44 struct xylon_drm_plane_properties properties;
45 dma_addr_t paddr;
46 u32 x;
47 u32 y;
48 unsigned int id;
49};
50
51struct xylon_drm_plane_manager {
52 struct drm_device *dev;
53 struct xylon_cvc *cvc;
54 struct xylon_drm_plane **plane;
55 int planes;
56};
57
58#define to_xylon_plane(x) container_of(x, struct xylon_drm_plane, base)
59
60static void
61xylon_drm_plane_set_parameters(struct xylon_drm_plane *plane,
62 struct xylon_drm_plane_manager *manager,
63 dma_addr_t paddr, u32 x, u32 y)
64{
65 plane->paddr = paddr;
66 plane->x = x;
67 plane->y = y;
68
69 xylon_cvc_layer_set_address(manager->cvc, plane->id, plane->paddr,
70 plane->x, plane->y);
71}
72
73void xylon_drm_plane_dpms(struct drm_plane *base_plane, int dpms)
74{
75 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
76 struct xylon_drm_plane_manager *manager = plane->manager;
77
78 switch (dpms) {
79 case DRM_MODE_DPMS_ON:
80 xylon_cvc_layer_enable(manager->cvc, plane->id);
81 break;
82 default:
83 xylon_cvc_layer_disable(manager->cvc, plane->id);
84 break;
85 }
86}
87
88void xylon_drm_plane_commit(struct drm_plane *base_plane)
89{
90 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
91 struct xylon_drm_plane_manager *manager = plane->manager;
92
93 xylon_cvc_layer_update(manager->cvc, plane->id);
94}
95
96int xylon_drm_plane_fb_set(struct drm_plane *base_plane,
97 struct drm_framebuffer *fb,
98 int crtc_x, int crtc_y,
99 unsigned int crtc_w, unsigned int crtc_h,
100 u32 src_x, u32 src_y,
101 u32 src_w, u32 src_h)
102{
103 struct drm_gem_cma_object *cma_obj;
104 struct drm_gem_object *gem_obj;
105 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
106 struct xylon_drm_plane_manager *manager = plane->manager;
107 int id = plane->id;
108 int ret;
109
110 if (fb->pixel_format != base_plane->format_types[0]) {
111 DRM_ERROR("unsupported pixel format %08x %08x\n",
112 fb->pixel_format, base_plane->format_types[0]);
113 return -EINVAL;
114 }
115
116 gem_obj = xylon_drm_fb_get_gem_obj(fb);
117 if (!gem_obj) {
118 DRM_ERROR("failed get gem obj for fb\n");
119 return -EINVAL;
120 }
121
122 cma_obj = to_drm_gem_cma_obj(gem_obj);
123
124 DRM_DEBUG("paddr: %p, h: %d(%d), v: %d(%d), bpp: %d\n",
125 (void *)cma_obj->paddr, src_w, crtc_x, src_h, crtc_y,
126 fb->bits_per_pixel);
127
128 xylon_drm_plane_set_parameters(plane, manager, cma_obj->paddr,
129 src_x, src_y);
130
131 ret = xylon_cvc_layer_set_size_position(manager->cvc, id,
132 src_x, src_y, src_w, src_h,
133 crtc_x, crtc_y, crtc_w, crtc_h);
134 if (ret) {
135 DRM_ERROR("failed setting layer size parameters\n");
136 return -EINVAL;
137 }
138
139 return 0;
140}
141
142static int xylon_drm_plane_update(struct drm_plane *base_plane,
143 struct drm_crtc *crtc,
144 struct drm_framebuffer *fb,
145 int crtc_x, int crtc_y,
146 unsigned int crtc_w, unsigned int crtc_h,
147 u32 src_x, u32 src_y,
148 u32 src_w, u32 src_h)
149{
150 int ret;
151
152 if (base_plane->type == DRM_PLANE_TYPE_PRIMARY) {
153 crtc->x = crtc_x;
154 crtc->y = crtc_y;
155 }
156
157 ret = xylon_drm_plane_fb_set(base_plane, fb,
158 crtc_x, crtc_y, crtc_w, crtc_h,
159 src_x >> 16, src_y >> 16,
160 src_w >> 16, src_h >> 16);
161 if (ret) {
162 DRM_ERROR("failed update plane\n");
163 return ret;
164 }
165
166 xylon_drm_plane_commit(base_plane);
167
168 xylon_drm_plane_dpms(base_plane, DRM_MODE_DPMS_ON);
169
170 return 0;
171}
172
173static int xylon_drm_plane_disable(struct drm_plane *base_plane)
174{
175 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
176 struct xylon_drm_plane_manager *manager = plane->manager;
177
178 xylon_cvc_layer_disable(manager->cvc, plane->id);
179
180 xylon_drm_plane_set_parameters(plane, manager, 0, 0, 0);
181
182 return 0;
183}
184
185static void xylon_drm_plane_destroy(struct drm_plane *base_plane)
186{
187 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
188 struct xylon_drm_plane_manager *manager = plane->manager;
189 int id = plane->id;
190
191 xylon_cvc_layer_disable(manager->cvc, id);
192
193 drm_plane_cleanup(base_plane);
194}
195
196static int xylon_drm_plane_set_property(struct drm_plane *base_plane,
197 struct drm_property *property,
198 u64 value)
199{
200 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
201 struct xylon_drm_plane_properties *props = &plane->properties;
202 struct xylon_drm_plane_op op;
203 unsigned int val = (unsigned int)value;
204
205 if (property == props->color_transparency) {
206 op.id = XYLON_DRM_PLANE_OP_ID_COLOR_TRANSPARENCY;
207 op.param = (bool)val;
208 } else if (property == props->interlace) {
209 op.id = XYLON_DRM_PLANE_OP_ID_INTERLACE;
210 op.param = (bool)val;
211 } else if (property == props->transparency) {
212 op.id = XYLON_DRM_PLANE_OP_ID_TRANSPARENCY;
213 op.param = (u32)val;
214 } else if (property == props->transparent_color) {
215 op.id = XYLON_DRM_PLANE_OP_ID_TRANSPARENT_COLOR;
216 op.param = (u32)val;
217 } else {
218 return -EINVAL;
219 }
220
221 xylon_drm_plane_op(base_plane, &op);
222
223 return 0;
224}
225
226static const struct drm_plane_funcs xylon_drm_plane_funcs = {
227 .update_plane = xylon_drm_plane_update,
228 .disable_plane = xylon_drm_plane_disable,
229 .destroy = xylon_drm_plane_destroy,
230 .set_property = xylon_drm_plane_set_property,
231};
232
233static int xylon_drm_plane_create_properties(struct drm_plane *base_plane)
234{
235 struct drm_device *dev = base_plane->dev;
236 struct drm_mode_object *obj = &base_plane->base;
237 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
238 struct xylon_drm_plane_properties *props = &plane->properties;
239 int size;
240 bool bg_layer = xylon_cvc_get_info(plane->manager->cvc,
241 LOGICVC_INFO_BACKGROUND_LAYER,
242 0);
243 bool last_plane = xylon_cvc_get_info(plane->manager->cvc,
244 LOGICVC_INFO_LAST_LAYER,
245 plane->id);
246
247 if (bg_layer || !last_plane) {
248 size = xylon_drm_property_size(property_color_transparency);
249 if (xylon_drm_property_create_list(dev, obj,
250 &props->color_transparency,
251 property_color_transparency,
252 "color_transparency",
253 size))
254 return -EINVAL;
255 }
256 if (bg_layer || !last_plane) {
257 if (xylon_drm_property_create_range(dev, obj,
258 &props->transparency,
259 "transparency",
260 XYLON_DRM_PROPERTY_ALPHA_MIN,
261 XYLON_DRM_PROPERTY_ALPHA_MAX,
262 XYLON_DRM_PROPERTY_ALPHA_MAX))
263 return -EINVAL;
264 }
265 if (bg_layer || !last_plane) {
266 if (xylon_drm_property_create_range(dev, obj,
267 &props->transparent_color,
268 "transparent_color",
269 XYLON_DRM_PROPERTY_COLOR_MIN,
270 XYLON_DRM_PROPERTY_COLOR_MAX,
271 XYLON_DRM_PROPERTY_COLOR_MIN))
272 return -EINVAL;
273 }
274 size = xylon_drm_property_size(property_interlace);
275 if (xylon_drm_property_create_list(dev, obj,
276 &props->interlace,
277 property_interlace,
278 "interlace",
279 size))
280 return -EINVAL;
281
282 return 0;
283}
284
285static void
286xylon_drm_plane_properties_initial_value(struct drm_plane *base_plane)
287{
288 struct drm_mode_object *obj = &base_plane->base;
289 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
290 struct xylon_drm_plane_properties *props = &plane->properties;
291 struct xylon_drm_plane_op op;
292 bool val;
293
294 if (props->color_transparency) {
295 op.id = XYLON_DRM_PLANE_OP_ID_COLOR_TRANSPARENCY;
296 op.param = false;
297 xylon_drm_plane_op(base_plane, &op);
298
299 val = xylon_cvc_get_info(plane->manager->cvc,
300 LOGICVC_INFO_LAYER_COLOR_TRANSPARENCY,
301 plane->id);
302 drm_object_property_set_value(obj, props->color_transparency,
303 val);
304 }
305}
306
307static struct drm_plane *
308xylon_drm_plane_create(struct xylon_drm_plane_manager *manager,
309 unsigned int possible_crtcs, bool primary,
310 unsigned int primary_id)
311{
312 struct device *dev = manager->dev->dev;
313 struct xylon_drm_plane *plane;
314 struct xylon_cvc *cvc = manager->cvc;
315 enum drm_plane_type type;
316 u32 format;
317 int i, ret;
318
319 if (primary) {
320 i = primary_id;
321 } else {
322 for (i = 0; i < manager->planes; i++)
323 if (!manager->plane[i])
324 break;
325 }
326
327 if (i >= manager->planes) {
328 DRM_ERROR("failed get plane\n");
329 return ERR_PTR(-ENODEV);
330 }
331
332 plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
333 if (!plane) {
334 DRM_ERROR("failed allocate plane\n");
335 return ERR_PTR(-ENOMEM);
336 }
337
338 plane->id = i;
339
340 format = xylon_cvc_layer_get_format(cvc, i);
341 if (primary)
342 type = DRM_PLANE_TYPE_PRIMARY;
343 else
344 type = DRM_PLANE_TYPE_OVERLAY;
345
346 ret = drm_universal_plane_init(manager->dev, &plane->base,
347 possible_crtcs, &xylon_drm_plane_funcs,
348 &format, 1, type, NULL);
349 if (ret) {
350 DRM_ERROR("failed initialize plane\n");
351 goto err_init;
352 }
353 plane->manager = manager;
354 manager->plane[i] = plane;
355
356 xylon_drm_plane_create_properties(&plane->base);
357
358 xylon_drm_plane_properties_initial_value(&plane->base);
359
360 return &plane->base;
361
362err_init:
363 xylon_cvc_layer_disable(cvc, plane->id);
364
365 return ERR_PTR(ret);
366}
367
368void xylon_drm_plane_properties_restore(struct xylon_drm_plane_manager *manager)
369{
370 struct drm_mode_object *obj;
371 struct drm_plane *base_plane;
372 struct xylon_drm_plane *plane;
373 struct xylon_drm_plane_properties *props;
374 struct xylon_drm_plane_op op;
375 int i;
376
377 for (i = 0; i < manager->planes; i++) {
378 if (!manager->plane[i])
379 continue;
380
381 plane = manager->plane[i];
382 base_plane = &plane->base;
383 obj = &base_plane->base;
384 props = &plane->properties;
385
386 op.id = XYLON_DRM_PLANE_OP_ID_COLOR_TRANSPARENCY;
387 op.param = false;
388 xylon_drm_plane_op(base_plane, &op);
389 drm_object_property_set_value(obj, props->color_transparency,
390 false);
391
392 op.id = XYLON_DRM_PLANE_OP_ID_INTERLACE;
393 xylon_drm_plane_op(base_plane, &op);
394 drm_object_property_set_value(obj, props->interlace, false);
395
396 op.id = XYLON_DRM_PLANE_OP_ID_TRANSPARENCY;
397 op.param = XYLON_DRM_PROPERTY_ALPHA_MAX;
398 xylon_drm_plane_op(base_plane, &op);
399 drm_object_property_set_value(obj, props->transparency,
400 XYLON_DRM_PROPERTY_ALPHA_MAX);
401
402 op.id = XYLON_DRM_PLANE_OP_ID_TRANSPARENT_COLOR;
403 op.param = XYLON_DRM_PROPERTY_COLOR_MIN;
404 xylon_drm_plane_op(base_plane, &op);
405 drm_object_property_set_value(obj, props->transparent_color,
406 XYLON_DRM_PROPERTY_COLOR_MIN);
407 }
408}
409
410int xylon_drm_plane_create_all(struct xylon_drm_plane_manager *manager,
411 unsigned int possible_crtcs,
412 unsigned int primary_id)
413{
414 struct drm_plane *plane;
415 int i, ret;
416 bool primary;
417
418 for (i = 0; i < manager->planes; i++) {
419 if (manager->plane[i])
420 continue;
421
422 primary = false;
423 if (i == primary_id)
424 primary = true;
425
426 plane = xylon_drm_plane_create(manager, possible_crtcs,
427 primary, primary_id);
428 if (IS_ERR(plane)) {
429 DRM_ERROR("failed allocate plane\n");
430 ret = PTR_ERR(plane);
431 goto err_out;
432 }
433 }
434
435 return 0;
436
437err_out:
438 return ret;
439}
440
441struct drm_plane *
442xylon_drm_plane_get_base(struct xylon_drm_plane_manager *manager,
443 unsigned int id)
444{
445 return &manager->plane[id]->base;
446}
447
448bool xylon_drm_plane_check_format(struct xylon_drm_plane_manager *manager,
449 u32 format)
450{
451 struct drm_plane *base_plane;
452 int i;
453
454 for (i = 0; i < manager->planes; i++) {
455 if (manager->plane[i]) {
456 base_plane = &manager->plane[i]->base;
457 if (format == base_plane->format_types[0])
458 return true;
459 }
460 }
461
462 return false;
463}
464
465unsigned int xylon_drm_plane_get_bits_per_pixel(struct drm_plane *base_plane)
466{
467 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
468 struct xylon_drm_plane_manager *manager = plane->manager;
469
470 return xylon_cvc_layer_get_bits_per_pixel(manager->cvc, plane->id);
471}
472
473int xylon_drm_plane_op(struct drm_plane *base_plane,
474 struct xylon_drm_plane_op *op)
475{
476 struct xylon_drm_plane *plane = to_xylon_plane(base_plane);
477 struct xylon_drm_plane_manager *manager = plane->manager;
478 struct xylon_cvc *cvc = manager->cvc;
479 int id = plane->id;
480 int par;
481
482 switch (op->id) {
483 case XYLON_DRM_PLANE_OP_ID_BACKGROUND_COLOR:
484 xylon_cvc_layer_set_color_reg(cvc,
485 BACKGROUND_LAYER_ID,
486 op->param);
487 return 0;
488 case XYLON_DRM_PLANE_OP_ID_TRANSPARENCY:
489 xylon_cvc_layer_set_alpha(cvc, id, op->param);
490 return 0;
491 case XYLON_DRM_PLANE_OP_ID_TRANSPARENT_COLOR:
492 xylon_cvc_layer_set_color_reg(cvc, id, op->param);
493 return 0;
494 case XYLON_DRM_PLANE_OP_ID_COLOR_TRANSPARENCY:
495 if (op->param)
496 par = LOGICVC_LAYER_COLOR_TRANSPARENCY_ENABLE;
497 else
498 par = LOGICVC_LAYER_COLOR_TRANSPARENCY_DISABLE;
499 break;
500 case XYLON_DRM_PLANE_OP_ID_INTERLACE:
501 if (op->param)
502 par = LOGICVC_LAYER_INTERLACE_ENABLE;
503 else
504 par = LOGICVC_LAYER_INTERLACE_DISABLE;
505 break;
506 }
507
508 xylon_cvc_layer_ctrl(cvc, id, par);
509
510 return 0;
511}
512
513struct xylon_drm_plane_manager *
514xylon_drm_plane_probe_manager(struct drm_device *drm_dev,
515 struct xylon_cvc *cvc)
516{
517 struct device *dev = drm_dev->dev;
518 struct xylon_drm_plane **plane;
519 struct xylon_drm_plane_manager *manager;
520
521 manager = devm_kzalloc(dev, sizeof(*manager), GFP_KERNEL);
522 if (!manager)
523 return ERR_PTR(-ENOMEM);
524
525 manager->dev = drm_dev;
526 manager->cvc = cvc;
527 manager->planes = xylon_cvc_layer_get_total_count(cvc);
528
529 plane = devm_kzalloc(dev, sizeof(**plane) * manager->planes,
530 GFP_KERNEL);
531 if (!plane)
532 return ERR_PTR(-ENOMEM);
533
534 manager->plane = plane;
535
536 DRM_DEBUG("%d %s\n", manager->planes,
537 manager->planes == 1 ? "plane" : "planes");
538
539 return manager;
540}
541