xref: /OK3568_Linux_fs/external/xserver/hw/xfree86/drivers/modesetting/xv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  *  Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  */
14 
15 #ifdef HAVE_DIX_CONFIG_H
16 #include "dix-config.h"
17 #endif
18 
19 #include "driver.h"
20 #include "dumb_bo.h"
21 /*
22 #include "xf86.h"
23 */
24 
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/un.h>
30 #include <libdrm/drm_fourcc.h>
31 
32 #include <X11/extensions/Xv.h>
33 #include "fourcc.h"
34 
35 #ifndef DRM_FORMAT_NV12_10
36 #define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2')
37 #endif
38 
39 #define XVIMAGE_XRGB8888 \
40    { \
41         DRM_FORMAT_XRGB8888, \
42         XvRGB, \
43         LSBFirst, \
44         {'R','G','B','X', \
45           0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71}, \
46         32, \
47         XvPacked, \
48         1, \
49         24, 0xff0000, 0x00ff00, 0x0000ff, \
50         0, 0, 0, \
51         0, 0, 0, \
52         0, 0, 0, \
53         {'B','G','R', \
54           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, \
55         XvTopToBottom \
56    }
57 
58 #define NUM_FORMATS 4
59 
60 static XF86VideoFormatRec Formats[NUM_FORMATS] = {
61     {15, TrueColor}, {16, TrueColor}, {24, TrueColor}, {30, TrueColor}
62 };
63 
64 #define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
65 
66 XvAttributeRec ms_exa_xv_attributes[] = {
67     {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_CLIENT_ID"},
68     {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_HOR_STRIDE"},
69     {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_VER_STRIDE"},
70     {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_DRM_FOURCC"},
71     {0, 0, 0, NULL}
72 };
73 int ms_exa_xv_num_attributes = ARRAY_SIZE(ms_exa_xv_attributes) - 1;
74 
75 Atom msDmaClient, msDmaHorStride, msDmaVerStride, msDmaDrmFourcc;
76 
77 XvImageRec ms_exa_xv_images[] = {
78     XVIMAGE_NV12,
79     XVIMAGE_XRGB8888
80 };
81 int ms_exa_xv_num_images = ARRAY_SIZE(ms_exa_xv_images);
82 
83 #define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1))
84 #define ClipValue(v,min,max) ((v) < (min) ? (min) : (v) > (max) ? (max) : (v))
85 
86 #define XV_MAX_DMA_FD 3
87 
88 typedef struct {
89     uint32_t dma_client;
90     uint32_t dma_hor_stride;
91     uint32_t dma_ver_stride;
92     uint32_t dma_drm_fourcc;
93     int dma_socket_fd;
94 } ms_exa_port_private;
95 
96 static void
ms_exa_xv_set_dma_client(ms_exa_port_private * port_priv,uint32_t dma_client)97 ms_exa_xv_set_dma_client(ms_exa_port_private *port_priv, uint32_t dma_client)
98 {
99     struct sockaddr_un addr;
100 
101     // re-open socket to flush pending messages
102     if (port_priv->dma_client)
103         close(port_priv->dma_socket_fd);
104 
105     port_priv->dma_client = dma_client;
106 
107     if (!dma_client)
108         goto clear;
109 
110     port_priv->dma_socket_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0);
111     if (port_priv->dma_socket_fd < 0)
112         goto clear;
113 
114     addr.sun_family = AF_LOCAL;
115     snprintf(addr.sun_path, sizeof(addr.sun_path),
116              "/tmp/.xv_dma_client.%d", port_priv->dma_client);
117     addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
118 
119     unlink(addr.sun_path);
120     if (bind(port_priv->dma_socket_fd,
121              (struct sockaddr *)&addr, sizeof(addr)) < 0)
122         goto clear;
123 
124     chmod(addr.sun_path, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
125 
126     return;
127 clear:
128     if (port_priv->dma_socket_fd > 0) {
129         close(port_priv->dma_socket_fd);
130         port_priv->dma_socket_fd = 0;
131     }
132     port_priv->dma_client = 0;
133     port_priv->dma_hor_stride = 0;
134     port_priv->dma_ver_stride = 0;
135     port_priv->dma_drm_fourcc = 0;
136 }
137 
138 static void
ms_exa_xv_stop_video(ScrnInfoPtr pScrn,void * data,Bool cleanup)139 ms_exa_xv_stop_video(ScrnInfoPtr pScrn, void *data, Bool cleanup)
140 {
141     ms_exa_port_private *port_priv = data;
142 
143     if (!cleanup)
144         return;
145 
146     ms_exa_xv_set_dma_client(port_priv, 0);
147 }
148 
149 static int
ms_exa_xv_set_port_attribute(ScrnInfoPtr pScrn,Atom attribute,INT32 value,void * data)150 ms_exa_xv_set_port_attribute(ScrnInfoPtr pScrn,
151                              Atom attribute, INT32 value, void *data)
152 {
153     ms_exa_port_private *port_priv = data;
154 
155     if (attribute == msDmaClient)
156         ms_exa_xv_set_dma_client(port_priv, ClipValue(value, 0, 0xFFFFFFFF));
157     else if (attribute == msDmaHorStride)
158         port_priv->dma_hor_stride = ClipValue(value, 0, 0xFFFFFFFF);
159     else if (attribute == msDmaVerStride)
160         port_priv->dma_ver_stride = ClipValue(value, 0, 0xFFFFFFFF);
161     else if (attribute == msDmaDrmFourcc)
162         port_priv->dma_drm_fourcc = ClipValue(value, 0, 0xFFFFFFFF);
163     else
164         return BadMatch;
165 
166     return Success;
167 }
168 
169 static int
ms_exa_xv_get_port_attribute(ScrnInfoPtr pScrn,Atom attribute,INT32 * value,void * data)170 ms_exa_xv_get_port_attribute(ScrnInfoPtr pScrn,
171                              Atom attribute, INT32 *value, void *data)
172 {
173     ms_exa_port_private *port_priv = data;
174 
175     if (attribute == msDmaClient)
176         *value = port_priv->dma_client;
177     else if (attribute == msDmaHorStride)
178         *value = port_priv->dma_hor_stride;
179     else if (attribute == msDmaVerStride)
180         *value = port_priv->dma_ver_stride;
181     else if (attribute == msDmaDrmFourcc)
182         *value = port_priv->dma_drm_fourcc;
183     else
184         return BadMatch;
185 
186     return Success;
187 }
188 
189 static void
ms_exa_xv_query_best_size(ScrnInfoPtr pScrn,Bool motion,short vid_w,short vid_h,short drw_w,short drw_h,unsigned int * p_w,unsigned int * p_h,void * data)190 ms_exa_xv_query_best_size(ScrnInfoPtr pScrn,
191                           Bool motion,
192                           short vid_w, short vid_h,
193                           short drw_w, short drw_h,
194                           unsigned int *p_w, unsigned int *p_h, void *data)
195 {
196     *p_w = drw_w;
197     *p_h = drw_h;
198 }
199 
200 static int
ms_exa_xv_query_image_attributes(ScrnInfoPtr pScrn,int id,unsigned short * w,unsigned short * h,int * pitches,int * offsets)201 ms_exa_xv_query_image_attributes(ScrnInfoPtr pScrn,
202                                  int id,
203                                  unsigned short *w, unsigned short *h,
204                                  int *pitches, int *offsets)
205 {
206     int size = 0, tmp;
207 
208     if (offsets)
209         offsets[0] = 0;
210 
211     if (id != FOURCC_NV12 && id != DRM_FORMAT_XRGB8888)
212         return 0;
213 
214     *w = ALIGN(*w, 2);
215     *h = ALIGN(*h, 2);
216     size = ALIGN(*w, 4);
217     if (pitches)
218         pitches[0] = size;
219     size *= *h;
220     if (offsets)
221         offsets[1] = size;
222     tmp = ALIGN(*w, 4);
223     if (id == DRM_FORMAT_XRGB8888)
224         tmp *= 4;
225     if (pitches)
226         pitches[1] = tmp;
227     tmp *= (*h >> 1);
228     size += tmp;
229 
230     return size;
231 }
232 
233 static PixmapPtr
ms_exa_xv_create_dma_pixmap(ScrnInfoPtr scrn,ms_exa_port_private * port_priv,int id)234 ms_exa_xv_create_dma_pixmap(ScrnInfoPtr scrn,
235                             ms_exa_port_private *port_priv, int id)
236 {
237     modesettingPtr ms = modesettingPTR(scrn);
238     ScreenPtr screen = scrn->pScreen;
239     PixmapPtr pixmap = NULL;
240     struct dumb_bo *bo = NULL;
241     struct iovec iov;
242     struct msghdr msg;
243     struct cmsghdr *header;
244     char buf[CMSG_SPACE (sizeof (int))];
245     int dma_fds[XV_MAX_DMA_FD], num_dma_fd = 0;
246     int width, height, pitch, bpp, depth;
247     uint32_t drm_fourcc;
248 
249     if (!port_priv->dma_client || port_priv->dma_socket_fd <= 0)
250         return NULL;
251 
252     if (!port_priv->dma_hor_stride || !port_priv->dma_ver_stride)
253         goto err;
254 
255     iov.iov_base = buf;
256     iov.iov_len = 1;
257 
258     msg.msg_iov = &iov;
259     msg.msg_iovlen = 1;
260     msg.msg_name = NULL;
261     msg.msg_namelen = 0;
262 
263     num_dma_fd = 0;
264     while (1) {
265         msg.msg_control = buf;
266         msg.msg_controllen = sizeof(buf);
267 
268         if (recvmsg(port_priv->dma_socket_fd, &msg, 0) < 0)
269             break;
270 
271         /* End with a empty msg */
272         header = CMSG_FIRSTHDR(&msg);
273         if (!header)
274             break;
275 
276         for (; header != NULL; header = CMSG_NXTHDR(&msg, header)) {
277             if (header->cmsg_level != SOL_SOCKET
278                 || header->cmsg_type != SCM_RIGHTS
279                 || header->cmsg_len != CMSG_LEN(sizeof(int)))
280                 break;
281 
282             dma_fds[num_dma_fd++] = *((int *)CMSG_DATA(header));
283         }
284     }
285 
286     /* Only expect 1 buffer */
287     if (num_dma_fd != 1)
288         goto err;
289 
290     width = port_priv->dma_hor_stride;
291     height = port_priv->dma_ver_stride;
292 
293     drm_fourcc = port_priv->dma_drm_fourcc ? port_priv->dma_drm_fourcc : id;
294     if (drm_fourcc == DRM_FORMAT_XRGB8888) {
295         pitch = width * 4;
296         depth = bpp = 32;
297     } else {
298         pitch = width * 3 / 2;
299         depth = bpp = 12;
300 
301         /* HACK: Special depth for NV12_10 and NV16*/
302         if (drm_fourcc == DRM_FORMAT_NV12_10) {
303             depth = 10;
304         } else if (drm_fourcc == DRM_FORMAT_NV16) {
305             depth = 16;
306         }
307     }
308 
309     pixmap = drmmode_create_pixmap_header(screen, width, height,
310                                           depth, bpp, pitch, NULL);
311     if (!pixmap)
312         goto err;
313 
314     bo = dumb_get_bo_from_fd(ms->drmmode.fd, dma_fds[0],
315                              pitch, pitch * height);
316     if (!bo)
317         goto err_free_pixmap;
318 
319     if (!ms_exa_set_pixmap_bo(scrn, pixmap, bo, TRUE))
320         goto err_free_bo;
321 
322     goto out;
323 
324 err_free_bo:
325     dumb_bo_destroy(ms->drmmode.fd, bo);
326 err_free_pixmap:
327     screen->DestroyPixmap(pixmap);
328     pixmap = NULL;
329 err:
330     ErrorF("ms xv failed to import dma pixmap\n");
331     ms_exa_xv_set_dma_client(port_priv, 0);
332 out:
333     while (num_dma_fd--)
334         close(dma_fds[num_dma_fd]);
335 
336     return pixmap;
337 }
338 
339 static PixmapPtr
ms_exa_xv_create_pixmap(ScrnInfoPtr scrn,ms_exa_port_private * port_priv,int id,unsigned char * buf,short width,short height)340 ms_exa_xv_create_pixmap(ScrnInfoPtr scrn, ms_exa_port_private *port_priv,
341                         int id,
342                         unsigned char *buf,
343                         short width,
344                         short height)
345 {
346     ScreenPtr screen = scrn->pScreen;
347     PixmapPtr pixmap;
348     int pitch, bpp;
349 
350     if (id == FOURCC_NV12) {
351         pitch = ALIGN(width, 4) * 3 / 2;
352         bpp = 12;
353     } else {
354         pitch = ALIGN(width, 4) * 4;
355         bpp = 32;
356     }
357 
358     pixmap = drmmode_create_pixmap_header(screen, width, height,
359                                           bpp, bpp, pitch, buf);
360     if (!pixmap)
361         return NULL;
362 
363     pixmap->devKind = pitch;
364     pixmap->devPrivate.ptr = buf;
365 
366     return pixmap;
367 }
368 
369 static int
ms_exa_xv_put_image(ScrnInfoPtr pScrn,short src_x,short src_y,short drw_x,short drw_y,short src_w,short src_h,short drw_w,short drw_h,int id,unsigned char * buf,short width,short height,Bool sync,RegionPtr clipBoxes,void * data,DrawablePtr pDrawable)370 ms_exa_xv_put_image(ScrnInfoPtr pScrn,
371                     short src_x, short src_y,
372                     short drw_x, short drw_y,
373                     short src_w, short src_h,
374                     short drw_w, short drw_h,
375                     int id,
376                     unsigned char *buf,
377                     short width,
378                     short height,
379                     Bool sync,
380                     RegionPtr clipBoxes, void *data, DrawablePtr pDrawable)
381 {
382     ms_exa_port_private *port_priv = data;
383     ScreenPtr screen = pScrn->pScreen;
384     PixmapPtr src_pixmap, dst_pixmap;
385     pixman_f_transform_t transform;
386     double sx, sy, tx, ty;
387     int ret = Success;
388 
389     if (id != FOURCC_NV12 && id != DRM_FORMAT_XRGB8888)
390         return BadMatch;
391 
392     src_pixmap = ms_exa_xv_create_dma_pixmap(pScrn, port_priv, id);
393     if (!src_pixmap) {
394         src_pixmap = ms_exa_xv_create_pixmap(pScrn, port_priv, id,
395                                              buf, width, height);
396         if (!src_pixmap)
397             return BadMatch;
398     }
399 
400     if (pDrawable->type == DRAWABLE_WINDOW)
401         dst_pixmap = screen->GetWindowPixmap((WindowPtr) pDrawable);
402     else
403         dst_pixmap = (PixmapPtr) pDrawable;
404 
405     DamageRegionAppend(pDrawable, clipBoxes);
406 
407     sx = (double)src_w / drw_w;
408     sy = (double)src_h / drw_h;
409 
410     tx = drw_x - src_x;
411     ty = drw_y - src_y;
412 
413 #ifdef COMPOSITE
414     RegionTranslate(clipBoxes, -dst_pixmap->screen_x, -dst_pixmap->screen_y);
415     tx -= dst_pixmap->screen_x;
416     ty -= dst_pixmap->screen_y;
417 #endif
418 
419     pixman_f_transform_init_scale(&transform, sx, sy);
420     pixman_f_transform_translate(NULL, &transform, tx, ty);
421 
422     if (!ms_exa_copy_area(src_pixmap, dst_pixmap, &transform, clipBoxes))
423         ret = BadMatch;
424 
425     DamageRegionProcessPending(pDrawable);
426 
427     screen->DestroyPixmap(src_pixmap);
428 
429     return ret;
430 }
431 
432 static XF86VideoEncodingRec DummyEncoding[1] = {
433     { 0, "XV_IMAGE", 8192, 8192, {1, 1} }
434 };
435 
436 XF86VideoAdaptorPtr
ms_exa_xv_init(ScreenPtr screen,int num_texture_ports)437 ms_exa_xv_init(ScreenPtr screen, int num_texture_ports)
438 {
439     ms_exa_port_private *port_priv;
440     XF86VideoAdaptorPtr adapt;
441     int i;
442 
443     msDmaClient = MAKE_ATOM("XV_DMA_CLIENT_ID");
444     msDmaHorStride = MAKE_ATOM("XV_DMA_HOR_STRIDE");
445     msDmaVerStride = MAKE_ATOM("XV_DMA_VER_STRIDE");
446     msDmaDrmFourcc = MAKE_ATOM("XV_DMA_DRM_FOURCC");
447 
448     adapt = calloc(1, sizeof(XF86VideoAdaptorRec) + num_texture_ports *
449                    (sizeof(ms_exa_port_private) + sizeof(DevUnion)));
450     if (adapt == NULL)
451         return NULL;
452 
453     adapt->type = XvWindowMask | XvInputMask | XvImageMask;
454     adapt->flags = 0;
455     adapt->name = "Modesetting Textured Video";
456     adapt->nEncodings = 1;
457     adapt->pEncodings = DummyEncoding;
458 
459     adapt->nFormats = NUM_FORMATS;
460     adapt->pFormats = Formats;
461     adapt->nPorts = num_texture_ports;
462     adapt->pPortPrivates = (DevUnion *) (&adapt[1]);
463 
464     adapt->pAttributes = ms_exa_xv_attributes;
465     adapt->nAttributes = ms_exa_xv_num_attributes;
466 
467     port_priv =
468         (ms_exa_port_private *) (&adapt->pPortPrivates[num_texture_ports]);
469 
470     adapt->pImages = ms_exa_xv_images;
471     adapt->nImages = ms_exa_xv_num_images;
472 
473     adapt->PutVideo = NULL;
474     adapt->PutStill = NULL;
475     adapt->GetVideo = NULL;
476     adapt->GetStill = NULL;
477     adapt->StopVideo = ms_exa_xv_stop_video;
478     adapt->SetPortAttribute = ms_exa_xv_set_port_attribute;
479     adapt->GetPortAttribute = ms_exa_xv_get_port_attribute;
480     adapt->QueryBestSize = ms_exa_xv_query_best_size;
481     adapt->PutImage = ms_exa_xv_put_image;
482     adapt->ReputImage = NULL;
483     adapt->QueryImageAttributes = ms_exa_xv_query_image_attributes;
484 
485     for (i = 0; i < num_texture_ports; i++) {
486         ms_exa_port_private *priv = &port_priv[i];
487 
488         priv->dma_client = 0;
489         priv->dma_socket_fd = 0;
490         priv->dma_hor_stride = 0;
491         priv->dma_ver_stride = 0;
492         priv->dma_drm_fourcc = 0;
493 
494         adapt->pPortPrivates[i].ptr = (void *) (priv);
495     }
496     return adapt;
497 }
498