1 /*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Rob Clark <rob.clark@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include "util.h"
24 #include <drm.h>
26 /* Dynamic debug. */
27 int debug = 0;
29 void disp_kms_usage(void);
30 struct display * disp_kms_open(int argc, char **argv);
32 #ifdef HAVE_X11
33 void disp_x11_usage(void);
34 struct display * disp_x11_open(int argc, char **argv);
35 void disp_x11_close(struct display *disp);
36 #endif
38 #ifdef HAVE_KMSCUBE
39 void disp_kmscube_usage(void);
40 struct display * disp_kmscube_open(int argc, char **argv);
41 #endif
43 void
44 disp_usage(void)
45 {
46 MSG("Generic Display options:");
47 MSG("\t--debug\tTurn on debug messages.");
48 MSG("\t--fps <fps>\tforce playback rate (0 means \"do not force\")");
49 MSG("\t--no-post\tDo not post buffers (disables screen updates) for benchmarking. Rate can still be controlled.");
51 #ifdef HAVE_X11
52 disp_x11_usage();
53 #endif
54 #ifdef HAVE_KMSCUBE
55 disp_kmscube_usage();
56 #endif
57 disp_kms_usage();
58 }
60 static int
61 empty_post_buffer(struct display *disp, struct buffer *buf)
62 {
63 return 0;
64 }
66 static int
67 empty_post_vid_buffer(struct display *disp, struct buffer *buf,
68 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
69 {
70 return 0;
71 }
73 struct display *
74 disp_open(int argc, char **argv)
75 {
76 struct display *disp;
77 int i, fps = 0, no_post = 0;
79 for (i = 1; i < argc; i++) {
80 if (!argv[i]) {
81 continue;
82 }
83 if (!strcmp("--debug", argv[i])) {
84 debug = 1;
85 MSG("Enabling dynamic debug.");
86 argv[i] = NULL;
88 } else if (!strcmp("--fps", argv[i])) {
89 argv[i++] = NULL;
91 if (sscanf(argv[i], "%d", &fps) != 1) {
92 ERROR("invalid arg: %s", argv[i]);
93 return NULL;
94 }
96 MSG("Forcing playback rate at %d fps.", fps);
97 argv[i] = NULL;
99 } else if (!strcmp("--no-post", argv[i])) {
100 MSG("Disabling buffers posting.");
101 no_post = 1;
102 argv[i] = NULL;
103 }
104 }
106 #ifdef HAVE_X11
107 disp = disp_x11_open(argc, argv);
108 if (disp)
109 goto out;
110 #endif
111 #ifdef HAVE_KMSCUBE
112 disp = disp_kmscube_open(argc, argv);
113 if (disp)
114 goto out;
115 #endif
117 disp = disp_kms_open(argc, argv);
119 if (!disp) {
120 ERROR("unable to create display");
121 return NULL;
122 }
124 out:
125 disp->rtctl.fps = fps;
127 /* If buffer posting is disabled from command line, override post
128 * functions with empty ones. */
129 if (no_post) {
130 disp->post_buffer = empty_post_buffer;
131 disp->post_vid_buffer = empty_post_vid_buffer;
132 }
134 return disp;
135 }
137 struct buffer **
138 disp_get_vid_buffers(struct display *disp, uint32_t n,
139 uint32_t fourcc, uint32_t w, uint32_t h)
140 {
141 struct buffer **buffers;
142 unsigned int i;
144 buffers = disp->get_vid_buffers(disp, n, fourcc, w, h);
145 if (buffers) {
146 /* if allocation succeeded, store in the unlocked
147 * video buffer list
148 */
149 list_init(&disp->unlocked);
150 for (i = 0; i < n; i++)
151 list_add(&buffers[i]->unlocked, &disp->unlocked);
152 }
154 return buffers;
155 }
157 void
158 disp_free_buffers(struct display *disp, uint32_t n)
159 {
160 disp->disp_free_buf(disp, n);
161 }
163 struct buffer *
164 disp_get_vid_buffer(struct display *disp)
165 {
166 struct buffer *buf = NULL;
167 if (!list_is_empty(&disp->unlocked)) {
168 buf = list_last_entry(&disp->unlocked, struct buffer, unlocked);
169 list_del(&buf->unlocked);
171 /* barrier.. if we are using GPU blitting, we need to make sure
172 * that the GPU is finished:
173 */
174 omap_bo_cpu_prep(buf->bo[0], OMAP_GEM_WRITE);
175 omap_bo_cpu_fini(buf->bo[0], OMAP_GEM_WRITE);
176 }
177 return buf;
178 }
180 void
181 disp_put_vid_buffer(struct display *disp, struct buffer *buf)
182 {
183 list_add(&buf->unlocked, &disp->unlocked);
184 }
186 /* Maintain playback rate if fps > 0. */
187 static void maintain_playback_rate(struct rate_control *p)
188 {
189 long usecs_since_last_frame;
190 int usecs_between_frames, usecs_to_sleep;
192 if (p->fps <= 0)
193 return;
195 usecs_between_frames = 1000000 / p->fps;
196 usecs_since_last_frame = mark(&p->last_frame_mark);
197 DBG("fps: %.02f", 1000000.0 / usecs_since_last_frame);
198 usecs_to_sleep = usecs_between_frames - usecs_since_last_frame + p->usecs_to_sleep;
200 if (usecs_to_sleep < 0)
201 usecs_to_sleep = 0;
203 /* mark() has a limitation that >1s time deltas will make the whole
204 * loop diverge. Workaround that limitation by clamping our desired sleep time
205 * to a maximum. TODO: Remove when mark() is in better shape. */
206 if (usecs_to_sleep >= 1000000)
207 usecs_to_sleep = 999999;
209 /* We filter a bit our rate adaptation, to avoid being too "choppy".
210 * Adjust the "alpha" value as needed. */
211 p->usecs_to_sleep = ((67 * p->usecs_to_sleep) + (33 * usecs_to_sleep)) / 100;
213 if (p->usecs_to_sleep >= 1) {
214 DBG("sleeping %dus", p->usecs_to_sleep);
215 usleep(p->usecs_to_sleep);
216 }
217 }
219 /* flip to / post the specified buffer */
220 int
221 disp_post_buffer(struct display *disp, struct buffer *buf)
222 {
223 maintain_playback_rate(&disp->rtctl);
224 return disp->post_buffer(disp, buf);
225 }
227 /* flip to / post the specified video buffer */
228 int
229 disp_post_vid_buffer(struct display *disp, struct buffer *buf,
230 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
231 {
232 maintain_playback_rate(&disp->rtctl);
233 return disp->post_vid_buffer(disp, buf, x, y, w, h);
234 }
236 struct buffer *
237 disp_get_fb(struct display *disp)
238 {
239 struct buffer **bufs = disp_get_buffers(disp, 1);
240 if (!bufs)
241 return NULL;
242 fill(bufs[0], 42);
243 disp_post_buffer(disp, bufs[0]);
244 return bufs[0];
245 }
248 int
249 check_args(int argc, char **argv)
250 {
251 int i;
252 for (i = 1; i < argc; i++) {
253 if (argv[i]) {
254 ERROR("invalid arg: %s", argv[i]);
255 return -1;
256 }
257 }
258 return 0;
259 }
261 /* stolen from modetest.c */
262 static void
263 fillRGB4(char *virtual, int n, int width, int height, int stride)
264 {
265 int i, j;
266 /* paint the buffer with colored tiles */
267 for (j = 0; j < height; j++) {
268 uint32_t *fb_ptr = (uint32_t*)((char*)virtual + j * stride);
269 for (i = 0; i < width; i++) {
270 div_t d = div(n+i+j, width);
271 fb_ptr[i] =
272 0x00130502 * (d.quot >> 6) +
273 0x000a1120 * (d.rem >> 6);
274 }
275 }
276 }
279 /* swap these for big endian.. */
280 #define RED 2
281 #define GREEN 1
282 #define BLUE 0
284 static void
285 fill420(unsigned char *y, unsigned char *u, unsigned char *v,
286 int cs /*chroma pixel stride */,
287 int n, int width, int height, int stride)
288 {
289 int i, j;
291 /* paint the buffer with colored tiles, in blocks of 2x2 */
292 for (j = 0; j < height; j+=2) {
293 unsigned char *y1p = y + j * stride;
294 unsigned char *y2p = y1p + stride;
295 unsigned char *up = u + (j/2) * stride * cs / 2;
296 unsigned char *vp = v + (j/2) * stride * cs / 2;
298 for (i = 0; i < width; i+=2) {
299 div_t d = div(n+i+j, width);
300 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
301 unsigned char *rgbp = (unsigned char *)&rgb;
302 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
304 *(y2p++) = *(y1p++) = y;
305 *(y2p++) = *(y1p++) = y;
307 *up = (rgbp[BLUE] - y) * 0.565 + 128;
308 *vp = (rgbp[RED] - y) * 0.713 + 128;
309 up += cs;
310 vp += cs;
311 }
312 }
313 }
315 static void
316 fill422(unsigned char *virtual, int n, int width, int height, int stride)
317 {
318 int i, j;
319 /* paint the buffer with colored tiles */
320 for (j = 0; j < height; j++) {
321 uint8_t *ptr = (uint8_t*)((char*)virtual + j * stride);
322 for (i = 0; i < width; i++) {
323 div_t d = div(n+i+j, width);
324 uint32_t rgb = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
325 unsigned char *rgbp = (unsigned char *)&rgb;
326 unsigned char y = (0.299 * rgbp[RED]) + (0.587 * rgbp[GREEN]) + (0.114 * rgbp[BLUE]);
328 *(ptr++) = y;
329 *(ptr++) = (rgbp[BLUE] - y) * 0.565 + 128;
330 *(ptr++) = y;
331 *(ptr++) = (rgbp[RED] - y) * 0.713 + 128;
332 }
333 }
334 }
337 void
338 fill(struct buffer *buf, int n)
339 {
340 int i;
342 for (i = 0; i < buf->nbo; i++)
343 omap_bo_cpu_prep(buf->bo[i], OMAP_GEM_WRITE);
345 switch(buf->fourcc) {
346 case 0: {
347 assert(buf->nbo == 1);
348 fillRGB4(omap_bo_map(buf->bo[0]), n,
349 buf->width, buf->height, buf->pitches[0]);
350 break;
351 }
352 case FOURCC('Y','U','Y','V'): {
353 assert(buf->nbo == 1);
354 fill422(omap_bo_map(buf->bo[0]), n,
355 buf->width, buf->height, buf->pitches[0]);
356 break;
357 }
358 case FOURCC('N','V','1','2'): {
359 unsigned char *y, *u, *v;
360 assert(buf->nbo == 2);
361 y = omap_bo_map(buf->bo[0]);
362 u = omap_bo_map(buf->bo[1]);
363 v = u + 1;
364 fill420(y, u, v, 2, n, buf->width, buf->height, buf->pitches[0]);
365 break;
366 }
367 case FOURCC('I','4','2','0'): {
368 unsigned char *y, *u, *v;
369 assert(buf->nbo == 3);
370 y = omap_bo_map(buf->bo[0]);
371 u = omap_bo_map(buf->bo[1]);
372 v = omap_bo_map(buf->bo[2]);
373 fill420(y, u, v, 1, n, buf->width, buf->height, buf->pitches[0]);
374 break;
375 }
376 default:
377 ERROR("invalid format: 0x%08x", buf->fourcc);
378 break;
379 }
381 for (i = 0; i < buf->nbo; i++)
382 omap_bo_cpu_fini(buf->bo[i], OMAP_GEM_WRITE);
383 }