xref: /OK3568_Linux_fs/external/gstreamer-rockchip/gst/kmssrc/gstkmssrc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright 2022 Rockchip Electronics Co., Ltd
3  *     Author: Jeffy Chen <jeffy.chen@rock-chips.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 
29 #include <xf86drm.h>
30 #include <xf86drmMode.h>
31 #include <drm_fourcc.h>
32 
33 #include <gst/video/video.h>
34 #include <gst/base/gstpushsrc.h>
35 #include <gst/allocators/gstdmabuf.h>
36 
37 #ifndef DRM_FORMAT_NV12_10
38 #define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2')
39 #endif
40 
41 #ifndef DRM_FORMAT_NV15
42 #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
43 #endif
44 
45 #define DEFAULT_PROP_FRAMERATE_LIMIT 120
46 
47 static gboolean DEFAULT_PROP_DMA_FEATURE = FALSE;
48 
49 #define GST_TYPE_KMS_SRC (gst_kms_src_get_type())
50 G_DECLARE_FINAL_TYPE (GstKmsSrc, gst_kms_src, GST, KMS_SRC, GstPushSrc);
51 
52 struct _GstKmsSrc {
53   GstBaseSrc element;
54 
55   GstAllocator *allocator;
56   GstVideoInfo info;
57 
58   gchar *devname;
59   gchar *bus_id;
60 
61   gint fd;
62 
63   guint crtc_id;
64   guint encoder_id;
65   guint connector_id;
66   guint plane_id;
67   guint fb_id;
68 
69   gboolean dma_feature;
70 
71   guint framerate_limit;
72   guint fps_n;
73   guint fps_d;
74   gboolean sync_fb;
75   gboolean sync_vblank;
76 
77   GstPoll *poll;
78   GstPollFD pollfd;
79 
80   guint last_fb_id;
81   GstClockTime last_frame_time;
82 
83   GstClockTime start_time;
84 };
85 
86 #define parent_class gst_kms_src_parent_class
87 G_DEFINE_TYPE (GstKmsSrc, gst_kms_src, GST_TYPE_PUSH_SRC);
88 
89 #define GST_KMS_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
90     GST_TYPE_KMS_SRC, GstKmsSrc))
91 
92 #define GST_CAT_DEFAULT kms_src_debug
93 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
94 
95 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
96     GST_PAD_SRC,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS_ANY);
99 
100 enum
101 {
102   PROP_DRIVER_NAME = 1,
103   PROP_BUS_ID,
104   PROP_CRTC_ID,
105   PROP_ENCODER_ID,
106   PROP_CONNECTOR_ID,
107   PROP_PLANE_ID,
108   PROP_FB_ID,
109   PROP_DMA_FEATURE,
110   PROP_FRAMERATE_LIMIT,
111   PROP_SYNC_FB,
112   PROP_SYNC_VBLANK,
113   PROP_LAST,
114 };
115 
116 struct kmssrc_fb {
117   guint handles[4];
118   guint pitches[4];
119   guint offsets[4];
120   guint width;
121   guint height;
122   guint fourcc;
123 };
124 
125 static void
gst_kms_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)126 gst_kms_src_set_property (GObject * object, guint prop_id,
127     const GValue * value, GParamSpec * pspec)
128 {
129   GstKmsSrc *self = GST_KMS_SRC (object);
130 
131   switch (prop_id) {
132     case PROP_DRIVER_NAME:
133       g_free (self->devname);
134       self->devname = g_value_dup_string (value);
135       break;
136     case PROP_BUS_ID:
137       g_free (self->bus_id);
138       self->bus_id = g_value_dup_string (value);
139       break;
140     case PROP_CRTC_ID:
141       self->crtc_id = g_value_get_uint (value);
142       break;
143     case PROP_ENCODER_ID:
144       self->encoder_id = g_value_get_uint (value);
145       break;
146     case PROP_CONNECTOR_ID:
147       self->connector_id = g_value_get_uint (value);
148       break;
149     case PROP_PLANE_ID:
150       self->plane_id = g_value_get_uint (value);
151       break;
152     case PROP_FB_ID:
153       self->fb_id = g_value_get_uint (value);
154       break;
155     case PROP_DMA_FEATURE:
156       self->dma_feature = g_value_get_boolean (value);
157       break;
158     case PROP_FRAMERATE_LIMIT:
159       self->framerate_limit = g_value_get_uint (value);
160       break;
161     case PROP_SYNC_FB:
162       self->sync_fb = g_value_get_boolean (value);
163       break;
164     case PROP_SYNC_VBLANK:
165       self->sync_vblank = g_value_get_boolean (value);
166       break;
167     default:
168       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169       break;
170   }
171 }
172 
173 static void
gst_kms_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)174 gst_kms_src_get_property (GObject * object, guint prop_id, GValue * value,
175     GParamSpec * pspec)
176 {
177   GstKmsSrc *self = GST_KMS_SRC (object);
178 
179   switch (prop_id) {
180     case PROP_DRIVER_NAME:
181       g_value_set_string (value, self->devname);
182       break;
183     case PROP_BUS_ID:
184       g_value_set_string (value, self->bus_id);
185       break;
186     case PROP_CRTC_ID:
187       g_value_set_uint (value, self->crtc_id);
188       break;
189     case PROP_ENCODER_ID:
190       g_value_set_uint (value, self->encoder_id);
191       break;
192     case PROP_CONNECTOR_ID:
193       g_value_set_uint (value, self->connector_id);
194       break;
195     case PROP_PLANE_ID:
196       g_value_set_uint (value, self->plane_id);
197       break;
198     case PROP_FB_ID:
199       g_value_set_uint (value, self->fb_id);
200       break;
201     case PROP_DMA_FEATURE:
202       g_value_set_boolean (value, self->dma_feature);
203       break;
204     case PROP_FRAMERATE_LIMIT:
205       g_value_set_uint (value, self->framerate_limit);
206       break;
207     case PROP_SYNC_FB:
208       g_value_set_boolean (value, self->sync_fb);
209       break;
210     case PROP_SYNC_VBLANK:
211       g_value_set_boolean (value, self->sync_vblank);
212       break;
213     default:
214       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215       break;
216   }
217 }
218 
219 static guint
gst_kms_src_get_crtc_fb(GstKmsSrc * self,guint crtc_id)220 gst_kms_src_get_crtc_fb (GstKmsSrc * self, guint crtc_id)
221 {
222   drmModeCrtcPtr crtc;
223   guint fb_id;
224 
225   crtc = drmModeGetCrtc (self->fd, crtc_id);
226   if (!crtc)
227     return 0;
228 
229   fb_id = crtc->buffer_id;
230 
231   drmModeFreeCrtc (crtc);
232   return fb_id;
233 }
234 
235 static guint
gst_kms_src_get_encoder_fb(GstKmsSrc * self,guint encoder_id)236 gst_kms_src_get_encoder_fb (GstKmsSrc * self, guint encoder_id)
237 {
238   drmModeEncoderPtr encoder;
239   guint fb_id;
240 
241   encoder = drmModeGetEncoder (self->fd, encoder_id);
242   if (!encoder)
243       return 0;
244 
245   fb_id = gst_kms_src_get_crtc_fb (self, encoder->crtc_id);
246 
247   drmModeFreeEncoder (encoder);
248   return fb_id;
249 }
250 
251 static guint
gst_kms_src_get_connector_fb(GstKmsSrc * self,guint connector_id)252 gst_kms_src_get_connector_fb (GstKmsSrc * self, guint connector_id)
253 {
254   drmModeConnectorPtr connector;
255   guint fb_id;
256 
257   connector = drmModeGetConnector (self->fd, connector_id);
258   if (!connector)
259     return 0;
260 
261   fb_id = gst_kms_src_get_encoder_fb (self, connector->encoder_id);
262 
263   drmModeFreeConnector (connector);
264   return fb_id;
265 }
266 
267 static guint
gst_kms_src_get_plane_fb(GstKmsSrc * self,guint plane_id)268 gst_kms_src_get_plane_fb (GstKmsSrc * self, guint plane_id)
269 {
270   drmModePlanePtr plane;
271   guint fb_id;
272 
273   plane = drmModeGetPlane (self->fd, plane_id);
274   if (!plane)
275     return 0;
276 
277   fb_id = plane->fb_id;
278 
279   drmModeFreePlane (plane);
280   return fb_id;
281 }
282 
283 static guint
gst_kms_src_get_fb_id(GstKmsSrc * self)284 gst_kms_src_get_fb_id (GstKmsSrc * self)
285 {
286   if (self->fb_id)
287     return self->fb_id;
288 
289   if (self->plane_id)
290     return gst_kms_src_get_plane_fb (self, self->plane_id);
291 
292   if (self->connector_id)
293     return gst_kms_src_get_connector_fb (self, self->connector_id);
294 
295   if (self->encoder_id)
296     return gst_kms_src_get_encoder_fb (self, self->encoder_id);
297 
298   if (self->crtc_id)
299     return gst_kms_src_get_crtc_fb (self, self->crtc_id);
300 
301   return 0;
302 }
303 
304 static guint
gst_kms_src_get_encoder_crtc(GstKmsSrc * self,guint encoder_id)305 gst_kms_src_get_encoder_crtc (GstKmsSrc * self, guint encoder_id)
306 {
307   drmModeEncoderPtr encoder;
308   guint crtc_id;
309 
310   encoder = drmModeGetEncoder (self->fd, encoder_id);
311   if (!encoder)
312       return 0;
313 
314   crtc_id = encoder->crtc_id;
315 
316   drmModeFreeEncoder (encoder);
317   return crtc_id;
318 }
319 
320 static guint
gst_kms_src_get_connector_crtc(GstKmsSrc * self,guint connector_id)321 gst_kms_src_get_connector_crtc (GstKmsSrc * self, guint connector_id)
322 {
323   drmModeConnectorPtr connector;
324   guint crtc_id;
325 
326   connector = drmModeGetConnector (self->fd, connector_id);
327   if (!connector)
328     return 0;
329 
330   crtc_id = gst_kms_src_get_encoder_crtc (self, connector->encoder_id);
331 
332   drmModeFreeConnector (connector);
333   return crtc_id;
334 }
335 
336 static guint
gst_kms_src_get_plane_crtc(GstKmsSrc * self,guint plane_id)337 gst_kms_src_get_plane_crtc (GstKmsSrc * self, guint plane_id)
338 {
339   drmModePlanePtr plane;
340   guint crtc_id;
341 
342   plane = drmModeGetPlane (self->fd, plane_id);
343   if (!plane)
344     return 0;
345 
346   crtc_id = plane->crtc_id;
347 
348   drmModeFreePlane (plane);
349   return crtc_id;
350 }
351 
352 static guint
gst_kms_src_get_crtc_id(GstKmsSrc * self)353 gst_kms_src_get_crtc_id (GstKmsSrc * self)
354 {
355   if (self->crtc_id)
356     return self->crtc_id;
357 
358   if (self->plane_id)
359     return gst_kms_src_get_plane_crtc (self, self->plane_id);
360 
361   if (self->connector_id)
362     return gst_kms_src_get_connector_crtc (self, self->connector_id);
363 
364   if (self->encoder_id)
365     return gst_kms_src_get_encoder_crtc (self, self->encoder_id);
366 
367   return 0;
368 }
369 
370 static guint
gst_kms_src_get_crtc_pipe(GstKmsSrc * self,guint crtc_id)371 gst_kms_src_get_crtc_pipe (GstKmsSrc * self, guint crtc_id)
372 {
373   drmModeResPtr res;
374   guint crtc_pipe = 0;
375   gint i;
376 
377   if (!crtc_id)
378     return 0;
379 
380   res = drmModeGetResources (self->fd);
381   if (!res)
382     return 0;
383 
384   for (i = 0; i < res->count_crtcs; i++) {
385     if (res->crtcs[i] == crtc_id) {
386       crtc_pipe = i;
387       break;
388     }
389   }
390   drmModeFreeResources (res);
391   return crtc_pipe;
392 }
393 
394 static void
sync_handler(gint fd,guint frame,guint sec,guint usec,gpointer data)395 sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
396 {
397   gboolean *waiting;
398 
399   (void) fd;
400   (void) frame;
401   (void) sec;
402   (void) usec;
403 
404   waiting = data;
405   *waiting = FALSE;
406 }
407 
408 static gboolean
gst_kms_src_sync_vblank(GstKmsSrc * self)409 gst_kms_src_sync_vblank (GstKmsSrc * self)
410 {
411   gboolean waiting;
412   drmEventContext evctxt = {
413     .version = DRM_EVENT_CONTEXT_VERSION,
414     .vblank_handler = sync_handler,
415   };
416   drmVBlank vbl = {
417     .request = {
418           .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
419           .sequence = 1,
420           .signal = (gulong) & waiting,
421         },
422   };
423   guint crtc_id = gst_kms_src_get_crtc_id (self);
424   guint crtc_pipe = gst_kms_src_get_crtc_pipe (self, crtc_id);
425   gint ret;
426 
427   if (crtc_pipe == 1)
428     vbl.request.type |= DRM_VBLANK_SECONDARY;
429   else if (crtc_pipe > 1)
430     vbl.request.type |= crtc_pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
431 
432   waiting = TRUE;
433   ret = drmWaitVBlank (self->fd, &vbl);
434   if (ret)
435     return FALSE;
436 
437   while (waiting) {
438     do {
439       ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
440     } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
441 
442     ret = drmHandleEvent (self->fd, &evctxt);
443     if (ret)
444       return FALSE;
445   }
446 
447   GST_DEBUG_OBJECT (self, "sync vblank with CRTC: %d(%d)", crtc_id, crtc_pipe);
448   return TRUE;
449 }
450 
451 static guint
gst_kms_src_get_next_fb_id(GstKmsSrc * self)452 gst_kms_src_get_next_fb_id (GstKmsSrc * self)
453 {
454   GstClockTimeDiff diff;
455   gboolean sync_fb = self->sync_fb && !self->fb_id;
456   guint duration = 0;
457   guint fb_id;
458 
459   if (self->sync_vblank)
460     gst_kms_src_sync_vblank (self);
461 
462   if (self->fps_d && self->fps_n)
463     duration =
464         gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n);
465   else if (self->framerate_limit)
466     duration = GST_SECOND / self->framerate_limit;
467 
468   while (1) {
469     diff = GST_CLOCK_DIFF (self->last_frame_time, gst_util_get_timestamp ());
470 
471     fb_id = gst_kms_src_get_fb_id (self);
472     if (!fb_id)
473       return 0;
474 
475     if (!sync_fb || fb_id != self->last_fb_id) {
476       sync_fb = FALSE;
477 
478       if (diff >= duration)
479         return fb_id;
480     }
481 
482     g_usleep (1000);
483   }
484 
485   return 0;
486 }
487 
488 #ifndef HAS_DRM_CLOSE_HANDLE
489 /* From libdrm 2.4.109 : xf86drm.c */
490 static int
drmCloseBufferHandle(int fd,uint32_t handle)491 drmCloseBufferHandle(int fd, uint32_t handle)
492 {
493   struct drm_gem_close args;
494 
495   memset(&args, 0, sizeof(args));
496   args.handle = handle;
497   return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &args);
498 }
499 #endif
500 
501 static void
gst_kms_src_free_fb(GstKmsSrc * self,struct kmssrc_fb * fb)502 gst_kms_src_free_fb (GstKmsSrc * self, struct kmssrc_fb * fb)
503 {
504   guint i;
505 
506   for (i = 0; i < 4; i++) {
507     if (fb->handles[i])
508       drmCloseBufferHandle (self->fd, fb->handles[i]);
509 
510     fb->handles[i] = 0;
511   }
512 }
513 
514 static gboolean
gst_kms_src_update_info(GstKmsSrc * self,struct kmssrc_fb * fb)515 gst_kms_src_update_info (GstKmsSrc * self, struct kmssrc_fb * fb)
516 {
517   GstVideoInfo *info = &self->info;
518   GstVideoFormat format;
519   guint i;
520 
521 #define KMSSRC_CASE_FOURCC(fourcc, gst) \
522   case DRM_FORMAT_ ## fourcc: format = GST_VIDEO_FORMAT_ ## gst; break;
523 
524   switch (fb->fourcc) {
525     KMSSRC_CASE_FOURCC (ARGB8888, BGRA);
526     KMSSRC_CASE_FOURCC (XRGB8888, BGRx);
527     KMSSRC_CASE_FOURCC (ABGR8888, RGBA);
528     KMSSRC_CASE_FOURCC (XBGR8888, RGBx);
529     KMSSRC_CASE_FOURCC (RGB888, BGR);
530     KMSSRC_CASE_FOURCC (BGR888, RGB);
531     KMSSRC_CASE_FOURCC (YUV422, Y42B);
532     KMSSRC_CASE_FOURCC (NV16, NV16);
533     KMSSRC_CASE_FOURCC (NV61, NV61);
534     KMSSRC_CASE_FOURCC (UYVY, UYVY);
535     KMSSRC_CASE_FOURCC (YVYU, YVYU);
536     KMSSRC_CASE_FOURCC (YUYV, YUY2);
537     KMSSRC_CASE_FOURCC (YUV420, I420);
538     KMSSRC_CASE_FOURCC (YVU420, YV12);
539     KMSSRC_CASE_FOURCC (NV12, NV12);
540     KMSSRC_CASE_FOURCC (NV21, NV21);
541     KMSSRC_CASE_FOURCC (RGB565, RGB16);
542     KMSSRC_CASE_FOURCC (BGR565, BGR16);
543 #ifdef HAVE_NV12_10LE40
544     KMSSRC_CASE_FOURCC (NV15, NV12_10LE40);
545     KMSSRC_CASE_FOURCC (NV12_10, NV12_10LE40);
546 #endif
547   default:
548     GST_ERROR_OBJECT (self, "format not supported %x", fb->fourcc);
549     return FALSE;
550   }
551 
552   gst_video_info_set_format (info, format, fb->width, fb->height);
553 
554   GST_VIDEO_INFO_SIZE (info) = 0;
555   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
556     GST_VIDEO_INFO_PLANE_STRIDE (info, i) = fb->pitches[i];
557     GST_VIDEO_INFO_PLANE_OFFSET (info, i) = fb->offsets[i];
558 
559     GST_DEBUG_OBJECT (self, "plane %d, stride %d, offset %" G_GSIZE_FORMAT, i,
560         GST_VIDEO_INFO_PLANE_STRIDE (info, i),
561         GST_VIDEO_INFO_PLANE_OFFSET (info, i));
562 
563     GST_VIDEO_INFO_SIZE (info) += fb->pitches[i] * fb->height;
564   }
565   GST_DEBUG_OBJECT (self, "size %" G_GSIZE_FORMAT, GST_VIDEO_INFO_SIZE (info));
566 
567   return TRUE;
568 }
569 
570 static gboolean
gst_kms_src_get_fb(GstKmsSrc * self,guint fb_id,struct kmssrc_fb * kmssrc_fb)571 gst_kms_src_get_fb (GstKmsSrc * self, guint fb_id, struct kmssrc_fb * kmssrc_fb)
572 {
573   drmModeFBPtr fb;
574 #ifdef HAS_DRM_MODE_FB2
575   drmModeFB2Ptr fb2;
576   guint i;
577 #endif
578 
579   memset (kmssrc_fb, 0, sizeof (*kmssrc_fb));
580 
581 #ifdef HAS_DRM_MODE_FB2
582   fb2 = drmModeGetFB2 (self->fd, fb_id);
583   if (fb2) {
584     for (i = 0; i < 4; i++) {
585       kmssrc_fb->handles[i] = fb2->handles[i];
586       kmssrc_fb->pitches[i] = fb2->pitches[i];
587       kmssrc_fb->offsets[i] = fb2->offsets[i];
588     }
589     kmssrc_fb->fourcc = fb2->pixel_format;
590     kmssrc_fb->width = fb2->width;
591     kmssrc_fb->height = fb2->height;
592     drmModeFreeFB2 (fb2);
593     return TRUE;
594   }
595 #endif
596 
597   fb = drmModeGetFB (self->fd, fb_id);
598   if (!fb)
599     return FALSE;
600 
601   kmssrc_fb->handles[0] = fb->handle;
602   kmssrc_fb->pitches[0] = fb->pitch;
603 
604   switch (fb->bpp) {
605   case 32:
606     if (fb->depth == 32)
607       kmssrc_fb->fourcc = DRM_FORMAT_ARGB8888;
608     else
609       kmssrc_fb->fourcc = DRM_FORMAT_XRGB8888;
610     break;
611   case 16:
612     kmssrc_fb->fourcc = DRM_FORMAT_RGB565;
613     break;
614   default:
615     GST_ERROR_OBJECT (self, "FB format unsupported");
616     gst_kms_src_free_fb (self, kmssrc_fb);
617     drmModeFreeFB (fb);
618     return FALSE;
619   }
620 
621   kmssrc_fb->width = fb->width;
622   kmssrc_fb->height = fb->height;
623   drmModeFreeFB (fb);
624   return TRUE;
625 }
626 
627 static GstBuffer *
gst_kms_src_import_drm_fb(GstKmsSrc * self,guint fb_id)628 gst_kms_src_import_drm_fb (GstKmsSrc * self, guint fb_id)
629 {
630   GstVideoInfo *info = &self->info;
631   GstBuffer *buf = NULL;
632   GstMemory *mem;
633   struct kmssrc_fb fb;
634   struct stat st[4];
635   gint i, dmafd[4], size[4];
636 
637   if (!gst_kms_src_get_fb (self, fb_id, &fb)) {
638     GST_ERROR_OBJECT (self, "could not get DRM FB %d", fb_id);
639     return NULL;
640   }
641 
642   if (!gst_kms_src_update_info (self, &fb))
643     goto err;
644 
645   buf = gst_buffer_new ();
646   if (!buf)
647     goto err;
648 
649   for (i = 0; i < 4; i++) {
650     if (!fb.handles[i])
651       break;
652 
653     size[i] = fb.pitches[i] * fb.height;
654 
655     GST_DEBUG_OBJECT (self, "importing DRM handle %d(%d) for %dx%d",
656         fb.handles[i], i, fb.pitches[i], fb.height);
657 
658     if (drmPrimeHandleToFD (self->fd, fb.handles[i], DRM_CLOEXEC | DRM_RDWR,
659           &dmafd[i]) < 0) {
660       GST_ERROR_OBJECT (self, "could not import DRM handle %d", fb.handles[i]);
661       goto err;
662     }
663 
664     fstat (dmafd[i], &st[i]);
665     if (i && !memcmp (&st[i], &st[i - 1], sizeof (struct stat))) {
666       /* Reuse the old fd */
667       close (dmafd[i]);
668       dmafd[i] = dmafd[i - 1];
669 
670       /* Check for contig planes */
671       if (fb.offsets[i] == fb.offsets[i - 1] + size[i - 1]) {
672         gsize mem_size, mem_offset;
673 
674         mem_size = gst_memory_get_sizes (mem, &mem_offset, NULL);
675         gst_memory_resize (mem, mem_offset, mem_size + size[i]);
676         continue;
677       }
678     }
679 
680     mem = gst_fd_allocator_alloc (self->allocator, dmafd[i], st[i].st_size,
681               GST_FD_MEMORY_FLAG_NONE);
682     if (!mem) {
683       close (dmafd[i]);
684       goto err;
685     }
686 
687     gst_memory_resize (mem, fb.offsets[i], size[i]);
688     gst_buffer_append_memory (buf, mem);
689   }
690 
691   gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
692       GST_VIDEO_INFO_FORMAT (info),
693       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
694       GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
695 
696   gst_kms_src_free_fb (self, &fb);
697   return buf;
698 err:
699   if (buf)
700     gst_buffer_unref (buf);
701 
702   gst_kms_src_free_fb (self, &fb);
703   return NULL;
704 }
705 
706 static GstFlowReturn
gst_kms_src_create(GstPushSrc * src,GstBuffer ** ret)707 gst_kms_src_create (GstPushSrc * src, GstBuffer ** ret)
708 {
709   GstKmsSrc *self = GST_KMS_SRC (src);
710   GstBuffer *buf;
711   guint fb_id;
712 
713   GST_DEBUG_OBJECT (self, "creating buffer");
714 
715   fb_id = gst_kms_src_get_next_fb_id (self);
716   if (!fb_id) {
717     GST_ERROR_OBJECT (self, "could not get valid FB");
718     goto err;
719   }
720 
721   GST_DEBUG_OBJECT (self, "importing DRM FB %d (old: %d)",
722       fb_id, self->last_fb_id);
723 
724   buf = gst_kms_src_import_drm_fb (self, fb_id);
725   if (!buf) {
726     GST_ERROR_OBJECT (self, "could not import FB %d", fb_id);
727     goto err;
728   }
729 
730   self->last_frame_time = gst_util_get_timestamp ();
731   self->last_fb_id = fb_id;
732 
733   GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) =
734       self->last_frame_time - self->start_time;
735 
736   *ret = buf;
737 
738   return GST_FLOW_OK;
739 err:
740   if (self->last_fb_id)
741     return GST_FLOW_EOS;
742 
743   return GST_FLOW_ERROR;
744 }
745 
746 /* Based on gst-plugins-good/sys/ximage/gstximagesrc.c */
747 static gboolean
gst_kms_src_set_caps(GstBaseSrc * basesrc,GstCaps * caps)748 gst_kms_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
749 {
750   GstKmsSrc *self = GST_KMS_SRC (basesrc);
751   GstVideoInfo *info = &self->info;
752   GstStructure *structure;
753   const GValue *new_fps;
754 
755   /* The only thing that can change is the framerate downstream wants */
756   structure = gst_caps_get_structure (caps, 0);
757   new_fps = gst_structure_get_value (structure, "framerate");
758   if (!new_fps)
759     return FALSE;
760 
761   /* Store this FPS for use when generating buffers */
762   info->fps_n = self->fps_n = gst_value_get_fraction_numerator (new_fps);
763   info->fps_d = self->fps_d = gst_value_get_fraction_denominator (new_fps);
764 
765   GST_DEBUG_OBJECT (self, "peer wants %d/%d fps", self->fps_n, self->fps_d);
766 
767   if (self->framerate_limit * self->fps_d > self->fps_n)
768     GST_WARNING_OBJECT (self, "framerate-limit ignored");
769 
770   if (self->sync_fb)
771     GST_WARNING_OBJECT (self, "sync-fb enabled, framerate might be inaccurate");
772 
773   return TRUE;
774 }
775 
776 static GstCaps *
gst_kms_src_get_caps(GstBaseSrc * basesrc,GstCaps * filter)777 gst_kms_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
778 {
779   GstKmsSrc *self = GST_KMS_SRC (basesrc);
780   GstStructure *s;
781   GstCaps *caps;
782   struct kmssrc_fb fb;
783   guint fb_id;
784 
785   fb_id = gst_kms_src_get_fb_id (self);
786   if (!fb_id)
787     return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (basesrc));
788 
789   if (!gst_kms_src_get_fb (self, fb_id, &fb)) {
790     GST_ERROR_OBJECT (self, "could not get DRM FB %d", fb_id);
791     return NULL;
792   }
793 
794   if (!gst_kms_src_update_info (self, &fb)) {
795     gst_kms_src_free_fb (self, &fb);
796     return NULL;
797   }
798 
799   gst_kms_src_free_fb (self, &fb);
800 
801   caps = gst_video_info_to_caps (&self->info);
802 
803   if (self->dma_feature)
804     gst_caps_set_features (caps, 0,
805         gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
806 
807   if (filter) {
808     GstCaps *intersection;
809 
810     intersection =
811       gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
812     gst_caps_unref (caps);
813     caps = intersection;
814   }
815 
816   s = gst_caps_get_structure (caps, 0);
817   gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT,
818       1, NULL);
819 
820   GST_DEBUG_OBJECT (self, "returning caps: %" GST_PTR_FORMAT, caps);
821   return caps;
822 }
823 
824 static gboolean
gst_kms_src_stop(GstBaseSrc * basesrc)825 gst_kms_src_stop (GstBaseSrc * basesrc)
826 {
827   GstKmsSrc *self = GST_KMS_SRC (basesrc);
828 
829   gst_poll_remove_fd (self->poll, &self->pollfd);
830   gst_poll_restart (self->poll);
831   gst_poll_fd_init (&self->pollfd);
832 
833   if (self->allocator)
834     g_object_unref (self->allocator);
835 
836   if (self->fd >= 0)
837     drmClose (self->fd);
838 
839   return TRUE;
840 }
841 
842 static guint
gst_kms_src_find_best_crtc(GstKmsSrc * self)843 gst_kms_src_find_best_crtc (GstKmsSrc * self)
844 {
845   drmModeCrtcPtr crtc;
846   drmModeResPtr res;
847   guint crtc_id;
848   gint i;
849 
850   res = drmModeGetResources (self->fd);
851   if (!res)
852     return 0;
853 
854   for (i = 0, crtc_id = 0; i < res->count_crtcs; i++) {
855     crtc = drmModeGetCrtc (self->fd, res->crtcs[i]);
856     if (crtc && crtc->mode_valid) {
857       drmModeFreeCrtc (crtc);
858       crtc_id = res->crtcs[i];
859       break;
860     }
861     drmModeFreeCrtc (crtc);
862   }
863   drmModeFreeResources (res);
864 
865   if (crtc_id)
866     GST_DEBUG_OBJECT (self, "using best CRTC %d", crtc_id);
867 
868   return crtc_id;
869 }
870 
871 static gboolean
gst_kms_src_start(GstBaseSrc * basesrc)872 gst_kms_src_start (GstBaseSrc * basesrc)
873 {
874   GstKmsSrc *self = GST_KMS_SRC (basesrc);
875 
876   self->allocator = gst_dmabuf_allocator_new ();
877   if (!self->allocator)
878     return FALSE;
879 
880   if (self->devname || self->bus_id)
881     self->fd = drmOpen (self->devname, self->bus_id);
882 
883   if (self->fd < 0)
884     self->fd = open ("/dev/dri/card0", O_RDWR | O_CLOEXEC);
885 
886   if (self->fd < 0) {
887     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
888         ("Could not open DRM module %s", GST_STR_NULL (self->devname)),
889         ("reason: %s (%d)", g_strerror (errno), errno));
890     gst_kms_src_stop (basesrc);
891     return FALSE;
892   }
893 
894   if (!self->fb_id && !self->plane_id && !self->connector_id &&
895       !self->encoder_id && !self->crtc_id) {
896     self->crtc_id = gst_kms_src_find_best_crtc (self);
897     if (!self->crtc_id) {
898       GST_ERROR_OBJECT (self, "could not find a valid CRTC");
899       gst_kms_src_stop (basesrc);
900       return FALSE;
901     }
902   }
903 
904   self->last_fb_id = 0;
905   self->last_frame_time = gst_util_get_timestamp ();
906   self->start_time = gst_util_get_timestamp ();
907 
908   self->pollfd.fd = self->fd;
909   gst_poll_add_fd (self->poll, &self->pollfd);
910   gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
911 
912   return TRUE;
913 }
914 
915 static void
gst_kms_src_finalize(GObject * object)916 gst_kms_src_finalize (GObject * object)
917 {
918   GstKmsSrc *self;
919 
920   self = GST_KMS_SRC (object);
921   g_clear_pointer (&self->devname, g_free);
922   g_clear_pointer (&self->bus_id, g_free);
923 
924   gst_poll_free (self->poll);
925 
926   G_OBJECT_CLASS (parent_class)->finalize (object);
927 }
928 
929 static void
gst_kms_src_init(GstKmsSrc * self)930 gst_kms_src_init (GstKmsSrc * self)
931 {
932   self->devname = g_strdup ("rockchip");
933 
934   self->fd = -1;
935 
936   self->crtc_id = 0;
937   self->encoder_id = 0;
938   self->connector_id = 0;
939   self->plane_id = 0;
940   self->fb_id = 0;
941 
942   self->framerate_limit = DEFAULT_PROP_FRAMERATE_LIMIT;
943   self->sync_fb = TRUE;
944   self->sync_vblank = TRUE;
945   self->fps_n = 0;
946   self->fps_d = 1;
947 
948   gst_video_info_init (&self->info);
949 
950   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
951   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
952 
953   gst_poll_fd_init (&self->pollfd);
954   self->poll = gst_poll_new (TRUE);
955 }
956 
957 static void
gst_kms_src_class_init(GstKmsSrcClass * klass)958 gst_kms_src_class_init (GstKmsSrcClass * klass)
959 {
960   GObjectClass *gobject_class;
961   GstElementClass *gstelement_class;
962   GstBaseSrcClass *gstbase_src_class;
963   GstPushSrcClass *gstpush_src_class;
964   const gchar *env;
965 
966   gobject_class = G_OBJECT_CLASS (klass);
967   gstelement_class = GST_ELEMENT_CLASS (klass);
968   gstbase_src_class = GST_BASE_SRC_CLASS (klass);
969   gstpush_src_class = GST_PUSH_SRC_CLASS (klass);
970 
971   gobject_class->finalize = gst_kms_src_finalize;
972   gobject_class->set_property = gst_kms_src_set_property;
973   gobject_class->get_property = gst_kms_src_get_property;
974 
975   g_object_class_install_property (gobject_class, PROP_DRIVER_NAME,
976       g_param_spec_string ("driver-name",
977       "device name", "DRM device driver name", NULL,
978       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
979 
980   g_object_class_install_property (gobject_class, PROP_BUS_ID,
981       g_param_spec_string ("bus-id", "Bus ID", "DRM bus ID", NULL,
982       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
983 
984   g_object_class_install_property (gobject_class, PROP_CRTC_ID,
985       g_param_spec_uint ("crtc-id", "DRM crtc ID",
986           "DRM crtc ID (0 = unspecified)",
987           0, G_MAXINT, 0,
988           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
989 
990   g_object_class_install_property (gobject_class, PROP_ENCODER_ID,
991       g_param_spec_uint ("encoder-id", "DRM plane ID",
992           "DRM encoder ID (0 = unspecified)",
993           0, G_MAXINT, 0,
994           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
995 
996   g_object_class_install_property (gobject_class, PROP_CONNECTOR_ID,
997       g_param_spec_uint ("connector-id", "DRM connector ID",
998           "DRM connector ID (0 = unspecified)",
999           0, G_MAXINT, 0,
1000           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1001 
1002   g_object_class_install_property (gobject_class, PROP_PLANE_ID,
1003       g_param_spec_uint ("plane-id", "DRM plane ID",
1004           "DRM plane ID (0 = unspecified)",
1005           0, G_MAXINT, 0,
1006           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1007 
1008   g_object_class_install_property (gobject_class, PROP_FB_ID,
1009       g_param_spec_uint ("fb-id", "DRM FB ID",
1010           "DRM FB ID (0 = unspecified)",
1011           0, G_MAXINT, 0,
1012           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1013 
1014   env = g_getenv ("GST_KMSSRC_DMA_FEATURE");
1015   if (env && !strcmp (env, "1"))
1016     DEFAULT_PROP_DMA_FEATURE = TRUE;
1017 
1018   g_object_class_install_property (gobject_class, PROP_DMA_FEATURE,
1019       g_param_spec_boolean ("dma-feature", "DMA feature",
1020           "Enable GST DMA feature", DEFAULT_PROP_DMA_FEATURE,
1021           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1022 
1023   g_object_class_install_property (gobject_class, PROP_FRAMERATE_LIMIT,
1024       g_param_spec_uint ("framerate-limit", "Limited framerate",
1025           "Limited framerate",
1026           0, G_MAXINT, DEFAULT_PROP_FRAMERATE_LIMIT,
1027           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1028 
1029   g_object_class_install_property (gobject_class, PROP_SYNC_FB,
1030       g_param_spec_boolean ("sync-fb", "Sync with FB flip",
1031           "Sync with FB flip", TRUE,
1032           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1033 
1034   g_object_class_install_property (gobject_class, PROP_SYNC_VBLANK,
1035       g_param_spec_boolean ("sync-vblank", "Sync with vblank",
1036           "Sync with vblank", TRUE,
1037           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1038 
1039   gst_element_class_set_static_metadata (gstelement_class,
1040       "KMS Video Source",
1041       "Source/Video",
1042       "KMS Video Source",
1043       "Jeffy Chen <jeffy.chen@rock-chips.com>");
1044 
1045   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
1046 
1047   gstbase_src_class->set_caps = GST_DEBUG_FUNCPTR (gst_kms_src_set_caps);
1048   gstbase_src_class->get_caps = GST_DEBUG_FUNCPTR (gst_kms_src_get_caps);
1049   gstbase_src_class->start = GST_DEBUG_FUNCPTR (gst_kms_src_start);
1050   gstbase_src_class->stop = GST_DEBUG_FUNCPTR (gst_kms_src_stop);
1051   gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_kms_src_create);
1052 }
1053 
1054 static gboolean
plugin_init(GstPlugin * plugin)1055 plugin_init (GstPlugin * plugin)
1056 {
1057   if (!gst_element_register (plugin, "kmssrc", GST_RANK_NONE,
1058           gst_kms_src_get_type ()))
1059     return FALSE;
1060 
1061   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "kmssrc", 0, "KmsSrc");
1062   return TRUE;
1063 }
1064 
1065 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1066     GST_VERSION_MINOR,
1067     kmssrc,
1068     "KMS Src Plugin",
1069     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
1070