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