Tizen 2.1 release
[platform/core/uifw/e17.git] / src / modules / wl_drm / e_drm_output.c
1 #include "e.h"
2 #include "e_mod_main.h"
3
4 EINTERN int 
5 e_drm_output_subpixel_convert(int value)
6 {
7    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
8
9    switch (value) 
10      {
11       case DRM_MODE_SUBPIXEL_NONE:
12         return WL_OUTPUT_SUBPIXEL_NONE;
13       case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
14         return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
15       case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
16         return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
17       case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
18         return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
19       case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
20         return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
21       case DRM_MODE_SUBPIXEL_UNKNOWN:
22       default:
23           return WL_OUTPUT_SUBPIXEL_UNKNOWN;
24      }
25 }
26
27 EINTERN Eina_Bool 
28 e_drm_output_add_mode(E_Drm_Output *output, drmModeModeInfo *info)
29 {
30    E_Drm_Output_Mode *mode;
31
32    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
33
34    if (!(mode = malloc(sizeof(E_Drm_Output_Mode)))) 
35      return EINA_FALSE;
36
37    mode->base.flags = 0;
38    mode->base.w = info->hdisplay;
39    mode->base.h = info->vdisplay;
40    mode->base.refresh = info->vrefresh;
41    mode->info = *info;
42
43    wl_list_insert(output->base.modes.prev, &mode->base.link);
44
45    return EINA_TRUE;
46 }
47
48 EINTERN void 
49 e_drm_output_set_modes(E_Drm_Compositor *dcomp)
50 {
51    E_Drm_Output *output;
52    E_Drm_Output_Mode *mode;
53    int ret = 0;
54
55    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
56
57    wl_list_for_each(output, &dcomp->base.outputs, base.link)
58      {
59         mode = (E_Drm_Output_Mode *)output->base.current;
60         ret = drmModeSetCrtc(dcomp->drm.fd, output->crtc_id, 
61                              output->fb_id[output->current ^ 1], 0, 0, 
62                              &output->conn_id, 1, &mode->info);
63         if (ret < 0)
64           printf("Failed to set drm mode: %m\n");
65      }
66 }
67
68 EINTERN void 
69 e_drm_output_scanout_buffer_destroy(struct wl_listener *listener, void *data __UNUSED__)
70 {
71    E_Drm_Output *output;
72
73    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
74
75    output = container_of(listener, E_Drm_Output, scanout_buffer_destroy_listener);
76    output->scanout_buffer = NULL;
77    if (!output->pending_scanout_buffer)
78      e_compositor_schedule_repaint(output->base.compositor);
79 }
80
81 EINTERN void 
82 e_drm_output_pending_scanout_buffer_destroy(struct wl_listener *listener, void *data __UNUSED__)
83 {
84    E_Drm_Output *output;
85
86    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
87
88    output = container_of(listener, E_Drm_Output, 
89                          pending_scanout_buffer_destroy_listener);
90    output->pending_scanout_buffer = NULL;
91    e_compositor_schedule_repaint(output->base.compositor);
92 }
93
94 EINTERN void 
95 e_drm_output_repaint(E_Output *base, pixman_region32_t *damage)
96 {
97    E_Drm_Output *output;
98    E_Drm_Compositor *dcomp;
99    E_Surface *es;
100    E_Sprite *sprite;
101    unsigned int fb_id = 0;
102    int ret = 0;
103
104    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
105
106    output = (E_Drm_Output *)base;
107    dcomp = (E_Drm_Compositor *)output->base.compositor;
108
109    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
110                              GL_RENDERBUFFER, output->rbo[output->current]);
111    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
112      return;
113
114    e_drm_output_prepare_scanout_surface(output);
115
116    wl_list_for_each_reverse(es, &dcomp->base.surfaces, link)
117      e_surface_draw(es, &output->base, damage);
118
119    glFlush();
120
121    output->current ^= 1;
122
123    if (output->pending_fs_surf_fb_id != 0)
124      fb_id = output->pending_fs_surf_fb_id;
125    else
126      fb_id = output->fb_id[output->current ^ 1];
127
128    if (drmModePageFlip(dcomp->drm.fd, output->crtc_id, fb_id, 
129                        DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
130      return;
131
132    wl_list_for_each(sprite, &dcomp->sprites, link)
133      {
134         unsigned int flags = 0;
135         drmVBlank vbl;
136
137         vbl.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
138         vbl.request.sequence = 1;
139         if (!e_sprite_crtc_supported(base, sprite->possible_crtcs))
140           continue;
141         ret = drmModeSetPlane(dcomp->drm.fd, sprite->plane_id, output->crtc_id,
142                               sprite->pending_fb_id, flags, sprite->dx, 
143                               sprite->dy, sprite->dw, sprite->dh, sprite->sx, 
144                               sprite->sy, sprite->sw, sprite->sh);
145         if (ret)
146           printf("Setplane Failed: %s\n", strerror(errno));
147
148         vbl.request.signal = (unsigned long)sprite;
149         ret = drmWaitVBlank(dcomp->drm.fd, &vbl);
150         if (ret)
151           printf("VBlank event request failed: %s\n", strerror(errno));
152      }
153 }
154
155 EINTERN void 
156 e_drm_output_destroy(E_Output *base)
157 {
158    E_Drm_Output *output;
159    E_Drm_Compositor *dcomp;
160    drmModeCrtcPtr ocrtc;
161    int i = 0;
162
163    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
164
165    output = (E_Drm_Output *)base;
166    dcomp = (E_Drm_Compositor *)output->base.compositor;
167    ocrtc = output->orig_crtc;
168
169    /* TODO: backlight */
170    /* if (base->backlight) */
171
172    e_drm_output_set_cursor(&output->base, NULL);
173
174    drmModeSetCrtc(dcomp->drm.fd, ocrtc->crtc_id, ocrtc->buffer_id, 
175                   ocrtc->x, ocrtc->y, &output->conn_id, 1, &ocrtc->mode);
176    drmModeFreeCrtc(ocrtc);
177
178    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
179                              GL_RENDERBUFFER, 0);
180    glBindRenderbuffer(GL_RENDERBUFFER, 0);
181    glDeleteRenderbuffers(2, output->rbo);
182
183    for (i = 0; i < 2; i++)
184      {
185         drmModeRmFB(dcomp->drm.fd, output->fb_id[i]);
186         dcomp->base.destroy_image(dcomp->base.egl_display, output->image[i]);
187         gbm_bo_destroy(output->bo[i]);
188      }
189
190    dcomp->crtc_alloc &= ~(1 << output->crtc_id);
191    dcomp->conn_alloc &= ~(1 << output->conn_id);
192
193    e_output_destroy(&output->base);
194    wl_list_remove(&output->base.link);
195
196    free(output);
197 }
198
199 EINTERN void 
200 e_drm_output_assign_planes(E_Output *base)
201 {
202    E_Compositor *comp;
203    E_Surface *es;
204    pixman_region32_t overlap, soverlap;
205    E_Input_Device *dev;
206
207    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
208
209    comp = base->compositor;
210    pixman_region32_init(&overlap);
211    wl_list_for_each(es, &comp->surfaces, link)
212      {
213         pixman_region32_init(&soverlap);
214         pixman_region32_intersect(&soverlap, &overlap, &es->transform.box);
215         dev = (E_Input_Device *)comp->input_device;
216         if (es == dev->sprite)
217           {
218              e_drm_output_set_cursor_region(base, dev, &soverlap);
219              if (!dev->hw_cursor)
220                pixman_region32_union(&overlap, &overlap, &es->transform.box);
221           }
222         else if (!e_drm_output_prepare_overlay_surface(base, es, &soverlap))
223           {
224              pixman_region32_fini(&es->damage);
225              pixman_region32_init(&es->damage);
226           }
227         else
228           pixman_region32_union(&overlap, &overlap, &es->transform.box);
229
230         pixman_region32_fini(&soverlap);
231      }
232    pixman_region32_fini(&overlap);
233
234    e_drm_output_disable_sprites(base);
235 }
236
237 EINTERN void 
238 e_drm_output_set_dpms(E_Output *base, E_Dpms_Level level)
239 {
240    E_Drm_Output *output;
241    E_Compositor *comp;
242    E_Drm_Compositor *dcomp;
243    drmModeConnectorPtr conn;
244    drmModePropertyPtr prop;
245
246    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
247
248    output = (E_Drm_Output *)base;
249    comp = base->compositor;
250    dcomp = (E_Drm_Compositor *)comp;
251
252    if (!(conn = drmModeGetConnector(dcomp->drm.fd, output->conn_id)))
253      return;
254    if (!(prop = e_drm_output_get_property(dcomp->drm.fd, conn, "DPMS")))
255      {
256         drmModeFreeConnector(conn);
257         return;
258      }
259    drmModeConnectorSetProperty(dcomp->drm.fd, conn->connector_id, 
260                                prop->prop_id, level);
261    drmModeFreeProperty(prop);
262    drmModeFreeConnector(conn);
263 }
264
265 EINTERN Eina_Bool 
266 e_drm_output_prepare_scanout_surface(E_Drm_Output *output)
267 {
268    E_Drm_Compositor *dcomp;
269    E_Surface *es;
270    EGLint hdl, stride;
271    int ret = 0;
272    unsigned int fb_id = 0;
273    struct gbm_bo *bo;
274
275    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
276
277    dcomp = (E_Drm_Compositor *)output->base.compositor;
278    es = container_of(dcomp->base.surfaces.next, E_Surface, link);
279
280    /* Need to verify output->region contained in surface opaque
281     * region.  Or maybe just that format doesn't have alpha. */
282    return EINA_TRUE;
283
284    if ((es->geometry.x != output->base.x) || 
285        (es->geometry.y != output->base.y) || 
286        (es->geometry.w != output->base.current->w) || 
287        (es->geometry.h != output->base.current->h) || 
288        (es->transform.enabled) || (es->image == EGL_NO_IMAGE_KHR))
289      return EINA_FALSE;
290
291    bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display, 
292                                      es->image, es->geometry.w, 
293                                      es->geometry.h, GBM_BO_USE_SCANOUT);
294    hdl = gbm_bo_get_handle(bo).s32;
295    stride = gbm_bo_get_pitch(bo);
296
297    gbm_bo_destroy(bo);
298
299    if (hdl == 0) return EINA_FALSE;
300
301    ret = drmModeAddFB(dcomp->drm.fd, output->base.current->w, 
302                       output->base.current->h, 24, 32, stride, hdl, &fb_id);
303    if (ret) return EINA_FALSE;
304
305    output->pending_fs_surf_fb_id = fb_id;
306
307    output->pending_scanout_buffer = es->buffer;
308    output->pending_scanout_buffer->busy_count++;
309
310    wl_signal_add(&output->pending_scanout_buffer->resource.destroy_signal,
311                   &output->pending_scanout_buffer_destroy_listener);
312
313    pixman_region32_fini(&es->damage);
314    pixman_region32_init(&es->damage);
315
316    return EINA_TRUE;
317 }
318
319 EINTERN void 
320 e_drm_output_disable_sprites(E_Output *base)
321 {
322    E_Compositor *comp;
323    E_Drm_Compositor *dcomp;
324    E_Drm_Output *output;
325    E_Sprite *s;
326    int ret = 0;
327
328    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
329
330    comp = base->compositor;
331    dcomp = (E_Drm_Compositor *)comp;
332    output = (E_Drm_Output *)base;
333
334    wl_list_for_each(s, &dcomp->sprites, link)
335      {
336         if (s->pending_fb_id) continue;
337         ret = drmModeSetPlane(dcomp->drm.fd, s->plane_id, output->crtc_id, 
338                               0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
339         if (ret)
340           printf("Failed to disable plane: %s\n", strerror(errno));
341         drmModeRmFB(dcomp->drm.fd, s->fb_id);
342         s->surface = NULL;
343         s->pending_surface = NULL;
344         s->fb_id = 0;
345         s->pending_fb_id = 0;
346      }
347 }
348
349 EINTERN drmModePropertyPtr 
350 e_drm_output_get_property(int fd, drmModeConnectorPtr conn, const char *name)
351 {
352    drmModePropertyPtr props;
353    int i = 0;
354
355    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
356
357    for (i = 0; i < conn->count_props; i++)
358      {
359         if (!(props = drmModeGetProperty(fd, conn->props[i])))
360           continue;
361         if (!strcmp(props->name, name)) return props;
362         drmModeFreeProperty(props);
363      }
364
365    return NULL;
366 }
367
368 EINTERN int 
369 e_drm_output_prepare_overlay_surface(E_Output *base, E_Surface *es, pixman_region32_t *overlap)
370 {
371    E_Compositor *comp;
372    E_Drm_Compositor *dcomp;
373    E_Sprite *s;
374    Eina_Bool found = EINA_FALSE;
375    EGLint hdl, stride;
376    struct gbm_bo *bo;
377    unsigned int fb_id = 0;
378    unsigned int hdls[4], pitches[4], offsets[4];
379    int ret = 0;
380    pixman_region32_t drect, srect;
381    pixman_box32_t *box;
382    unsigned int format;
383
384    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
385
386    comp = base->compositor;
387    dcomp = (E_Drm_Compositor *)comp;
388
389    if (dcomp->sprites_broken) return -1;
390    if (e_surface_is_primary(comp, es)) return -1;
391    if (es->image == EGL_NO_IMAGE_KHR) return -1;
392
393    if (!e_drm_output_surface_transform_supported(es))
394      return -1;
395    if (!e_drm_output_surface_overlap_supported(base, overlap))
396      return -1;
397
398    wl_list_for_each(s, &dcomp->sprites, link)
399      {
400         if (!e_sprite_crtc_supported(base, s->possible_crtcs))
401           continue;
402         if (!s->pending_fb_id)
403           {
404              found = EINA_TRUE;
405              break;
406           }
407      }
408
409    if (!found) return -1;
410
411    bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display, 
412                                      es->image, es->geometry.w, 
413                                      es->geometry.h, GBM_BO_USE_SCANOUT);
414    format = gbm_bo_get_format(bo);
415    hdl = gbm_bo_get_handle(bo).s32;
416    stride = gbm_bo_get_pitch(bo);
417
418    gbm_bo_destroy(bo);
419
420    if (!e_drm_output_surface_format_supported(s, format))
421      return -1;
422
423    if (!hdl) return -1;
424
425    hdls[0] = hdl;
426    pitches[0] = stride;
427    offsets[0] = 0;
428
429    ret = drmModeAddFB2(dcomp->drm.fd, es->geometry.w, es->geometry.h, 
430                        format, hdls, pitches, offsets, &fb_id, 0);
431    if (ret)
432      {
433         dcomp->sprites_broken = EINA_TRUE;
434         return -1;
435      }
436
437    if ((s->surface) && (s->surface != es))
438      {
439         E_Surface *os;
440
441         os = s->surface;
442         pixman_region32_fini(&os->damage);
443         pixman_region32_init_rect(&os->damage, os->geometry.x, os->geometry.y,
444                                   os->geometry.w, os->geometry.h);
445      }
446
447    s->pending_fb_id = fb_id;
448    s->pending_surface = es;
449    es->buffer->busy_count++;
450
451    pixman_region32_init(&drect);
452    pixman_region32_intersect(&drect, &es->transform.box, &base->region);
453    pixman_region32_translate(&drect, -base->x, -base->y);
454
455    box = pixman_region32_extents(&drect);
456    s->dx = box->x1;
457    s->dy = box->y1;
458    s->dw = (box->x2 - box->x1);
459    s->dh = (box->y2 - box->y1);
460    pixman_region32_fini(&drect);
461
462    pixman_region32_init(&srect);
463    pixman_region32_intersect(&srect, &es->transform.box, &base->region);
464    pixman_region32_translate(&srect, -es->geometry.x, -es->geometry.y);
465
466    box = pixman_region32_extents(&srect);
467    s->sx = box->x1 << 16;
468    s->sy = box->y1 << 16;
469    s->sw = (box->x2 - box->x1) << 16;
470    s->sh = (box->y2 - box->y1) << 16;
471    pixman_region32_fini(&srect);
472
473    wl_signal_add(&es->buffer->resource.destroy_signal,
474                   &s->pending_destroy_listener);
475
476    return 0;
477 }
478
479 EINTERN Eina_Bool 
480 e_drm_output_surface_transform_supported(E_Surface *es)
481 {
482    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
483
484    if ((es) && (es->transform.enabled)) return EINA_TRUE;
485    return EINA_FALSE;
486 }
487
488 EINTERN Eina_Bool 
489 e_drm_output_surface_overlap_supported(E_Output *base __UNUSED__, pixman_region32_t *overlap)
490 {
491    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
492
493    if (pixman_region32_not_empty(overlap)) return EINA_TRUE;
494    return EINA_FALSE;
495 }
496
497 EINTERN Eina_Bool 
498 e_drm_output_surface_format_supported(E_Sprite *s, unsigned int format)
499 {
500    unsigned int i = 0;
501
502    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
503
504    for (i = 0; i < s->format_count; i++)
505      if (s->formats[i] == format) return EINA_TRUE;
506
507    return EINA_FALSE;
508 }
509
510 EINTERN void 
511 e_drm_output_set_cursor_region(E_Output *output, E_Input_Device *device, pixman_region32_t *overlap)
512 {
513    pixman_region32_t cregion;
514    Eina_Bool prior = EINA_FALSE;
515
516    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
517
518    if (!device->sprite) return;
519    pixman_region32_init(&cregion);
520    pixman_region32_intersect(&cregion, &device->sprite->transform.box, 
521                              &output->region);
522    if (!pixman_region32_not_empty(&cregion))
523      {
524         e_drm_output_set_cursor(output, NULL);
525         goto out;
526      }
527
528    prior = device->hw_cursor;
529    if ((pixman_region32_not_empty(overlap)) || 
530        (!e_drm_output_set_cursor(output, device)))
531      {
532         if (prior)
533           {
534              e_surface_damage(device->sprite);
535              e_drm_output_set_cursor(output, NULL);
536           }
537         device->hw_cursor = EINA_FALSE;
538      }
539    else
540      {
541         if (!prior) e_surface_damage_below(device->sprite);
542         pixman_region32_fini(&device->sprite->damage);
543         pixman_region32_init(&device->sprite->damage);
544         device->hw_cursor = EINA_TRUE;
545      }
546
547 out:
548    pixman_region32_fini(&cregion);
549 }
550
551 EINTERN Eina_Bool 
552 e_drm_output_set_cursor(E_Output *output, E_Input_Device *device)
553 {
554    E_Drm_Output *doutput;
555    E_Drm_Compositor *dcomp;
556    EGLint hdl, stride;
557    int ret = -1;
558    struct gbm_bo *bo;
559
560    DLOGFN(__FILE__, __LINE__, __FUNCTION__);
561
562    doutput = (E_Drm_Output *)output;
563    dcomp = (E_Drm_Compositor *)doutput->base.compositor;
564
565    if (!device)
566      {
567         drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
568         return EINA_TRUE;
569      }
570
571    if (device->sprite->image == EGL_NO_IMAGE_KHR) return EINA_FALSE;
572
573    if ((device->sprite->geometry.w > 64) || 
574        (device->sprite->geometry.h > 64))
575      return EINA_FALSE;
576
577    bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display, 
578                                      device->sprite->image, 64, 64, 
579                                      GBM_BO_USE_CURSOR_64X64);
580    if (!bo) return EINA_FALSE;
581
582    hdl = gbm_bo_get_handle(bo).s32;
583    stride = gbm_bo_get_pitch(bo);
584    gbm_bo_destroy(bo);
585
586    if (stride != (64 * 4)) return EINA_FALSE;
587
588    if ((ret = drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, hdl, 64, 64)))
589      {
590         drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
591         return EINA_FALSE;
592      }
593
594    ret = drmModeMoveCursor(dcomp->drm.fd, doutput->crtc_id, 
595                            device->sprite->geometry.x - doutput->base.x,
596                            device->sprite->geometry.y - doutput->base.y);
597    if (ret)
598      {
599         drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
600         return EINA_FALSE;
601      }
602
603    return EINA_TRUE;
604 }