vo_glamo: sub.h was moved to sub directory in c9026cb3210205b07e2e068467a18ee40f9259a3
[mplayer/glamo.git] / libvo / vo_glamo.c
blobcaa40158d0e7cb8592862fc60b8b4c8ed2817986
1 /*
2 * VIDIX driver for SMedia Glamo chips.
4 * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
5 * Based on mpeg.c from glamo-utils, written by Chia-I Wu <olv@openmoko.org>
6 * Based on ffmpeg code by Fabrice Bellard.
8 * This file is part of MPlayer.
10 * MPlayer is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 or (at your option)
13 * version 3 of the License.
15 * MPlayer is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <time.h>
35 #include <errno.h>
36 #include <endian.h>
38 #include "drivers/libglamo/glamo.h"
39 #include "drivers/libglamo/hw.h"
40 #include "drivers/libglamo/dma.h"
41 #include "drivers/libglamo/blit.h"
43 #include "config.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
46 #include "mp_msg.h"
47 #include "sub/sub.h"
48 #include "libmpdemux/mpeg4_hdr.h"
49 #include "subopt-helper.h"
51 #ifdef X11_FULLSCREEN
52 #include "x11_common.h"
53 #endif
55 static const vo_info_t info = {
56 .name = "SMedia Glamo decoder driver",
57 .short_name = "glamo",
58 .author = "Andrzej Zaborowski <balrog@zabor.org>",
59 .comment = "Experimental, requires root"
62 const LIBVO_EXTERN(glamo)
64 static uint16_t device_id = 0;
66 static int query_format(uint32_t fourcc)
68 if (device_id != 0x3362)
69 return 0;
71 switch (fourcc) {
72 /* MPEG1 stream for our MPEG engine. */
73 case IMGFMT_MPEG4:
74 /* Decoded frames to blit onto the framebuffer using the 2D engine
75 * and bypassing MPEG block. */
76 case IMGFMT_I420:
77 case IMGFMT_YUY2:
78 case IMGFMT_RGB16: /* TODO */
79 return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
80 VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_FLIP;
83 return 0;
86 static struct blit_ctx_s {
87 struct glamo_blit_manager *blit;
88 int x;
89 int y;
90 int width;
91 int height;
92 int out_width;
93 int out_height;
94 void *image_buf;
96 int blit_started;
97 } bctx;
98 static void (*decode_fn)(mp_image_t *mpi);
100 static int draw_image(mp_image_t *mpi)
102 struct glamo_blit_image *image;
104 decode_fn(mpi);
106 image = glamo_blit_new_image(bctx.blit, bctx.image_buf,
107 bctx.width, bctx.height);
108 bctx.blit_started = 1;
109 glamo_blit_show_image(bctx.blit, image, bctx.x, bctx.y,
110 bctx.out_width, bctx.out_height);
111 glamo_blit_destroy_image(bctx.blit, image);
113 return 0;
116 static void flip_page(void)
120 static void draw_fb(int x0, int y0, int w, int h, unsigned char *src,
121 unsigned char *srca, int stride)
124 * TODO: draw this before the final blit so that we get rotation
125 * right. This will require writing to bctx.image_buf which may
126 * have already started being blitted.
128 uint8_t *dst = (uint8_t *) glamo_fb +
129 2 * (bctx.x + x0) + glamo_pitch * (bctx.y + y0);
131 if (bctx.blit_started) {
132 glamo_blit_wait(bctx.blit);
133 bctx.blit_started = 0;
136 vo_draw_alpha_rgb16(w, h, src, srca, stride, dst, glamo_pitch);
139 static void draw_osd(void)
141 vo_draw_text(bctx.out_width, bctx.out_height, draw_fb);
144 static int draw_slice(uint8_t *srcimg[], int stride[],
145 int w, int h, int x0, int y0)
147 return 0;
150 static int draw_frame(uint8_t *src[])
152 return VO_ERROR;
155 static void check_events(void)
157 #if 0
158 int e = vo_check_events();
160 mp_msg(MSGT_VO, MSGL_INFO, "got event %x\n", e);
161 #endif
164 static int get_image(mp_image_t *mpi)
166 mp_msg(MSGT_VO, MSGL_INFO, "get_image called\n");
167 /* TODO: for the non-MPEG4 IMGFMTs return addresses directly
168 * in the Glamo's mmaped memory. For IMGFMT_MPEG4 we don't gain
169 * anything because the vd_hwmp4 will have to copy all the data
170 * anyway. */
172 return VO_NOTIMPL;
175 static int glamo_window(mp_win_t *win)
177 bctx.x = win->x;
178 bctx.y = win->y;
179 bctx.out_width = win->w;
180 bctx.out_height = win->h;
182 return VO_TRUE;
185 static int control(uint32_t request, void *data)
187 switch (request) {
188 case VOCTRL_QUERY_FORMAT:
189 return query_format(*(uint32_t *) data);
190 case VOCTRL_DRAW_IMAGE:
191 return draw_image((mp_image_t *) data);
192 case VOCTRL_GET_IMAGE:
193 return get_image(data);
195 /* "xover" support */
196 case VOCTRL_XOVERLAY_SUPPORT:
197 case VOCTRL_XOVERLAY_SET_COLORKEY:
198 return VO_TRUE;
199 case VOCTRL_XOVERLAY_SET_WIN:
200 return glamo_window((mp_win_t *) data);
203 return VO_NOTIMPL;
206 static strarg_t fb_path = { 0, NULL };
207 static int mpeg_db_thr[2] = { 0, 0 };
208 static int mpeg_deblock;
209 static int angle = 0;
210 static enum glamo_blit_rotation rotate;
211 static int busywait = 0;
213 static opt_t glamo_subopts[] = {
214 { "deblock1", OPT_ARG_INT, &mpeg_db_thr[0], (opt_test_f) int_pos, 0 },
215 { "deblock2", OPT_ARG_INT, &mpeg_db_thr[1], (opt_test_f) int_pos, 0 },
216 { "rotate", OPT_ARG_INT, &angle, (opt_test_f) int_pos, 0 },
217 { "fb", OPT_ARG_STR, &fb_path, NULL, 0 },
218 { "nosleep", OPT_ARG_BOOL, &busywait, NULL, 0 },
220 { 0, 0, 0, 0, 0 }
223 static int preinit(const char *arg)
225 uint16_t dev_id, rev_id;
226 int err;
228 err = subopt_parse(arg, glamo_subopts);
229 if (err)
230 return err;
232 err = glamo_os_init(fb_path.str);
233 if (err)
234 return err;
236 mpeg_deblock = mpeg_db_thr[0] || mpeg_db_thr[1];
238 switch (angle % 360) {
239 case 0:
240 rotate = GLAMO_BLIT_ROTATION_0;
241 break;
242 case 90:
243 rotate = GLAMO_BLIT_ROTATION_90;
244 break;
245 case 180:
246 rotate = GLAMO_BLIT_ROTATION_180;
247 break;
248 case 270:
249 rotate = GLAMO_BLIT_ROTATION_270;
250 break;
251 default:
252 mp_msg(MSGT_VO, MSGL_ERR,
253 "unsupported angle: %i deg\n", angle);
254 exit(-1);
257 dev_id = GLAMO_IN_REG(GLAMO_REG_DEVICE_ID);
258 rev_id = GLAMO_IN_REG(GLAMO_REG_REVISION_ID);
259 glamo_os_finish();
261 switch (dev_id) {
262 case 0x3600:
263 case 0x3650:
264 case 0x3700:
265 device_id = 0x3362;
266 dev_id = 0x3000 | (dev_id >> 4);
267 break;
268 default:
269 return -ENODEV;
272 mp_msg(MSGT_VO, MSGL_INFO,
273 "Found SMedia Glamo %x, with %x core, rev A%i\n",
274 device_id, dev_id, rev_id);
275 return 0;
278 void inline st32le(void *ptr, uint32_t value)
280 #if __BYTE_ORDER == __LITTLE_ENDIAN
281 # if 0
282 memcpy(ptr, &value, 4);
283 # else
284 *(uint32_t *) ptr = value;
285 # endif
286 #else
287 ((uint8_t *) ptr)[0] = (value >> 0) & 0xff;
288 ((uint8_t *) ptr)[1] = (value >> 8) & 0xff;
289 ((uint8_t *) ptr)[2] = (value >> 16) & 0xff;
290 ((uint8_t *) ptr)[3] = (value >> 24) & 0xff;
291 #endif
294 static void yield1(void)
296 const struct timespec req = { 0, 500000 };
298 if (!busywait)
299 nanosleep(&req, 0);
302 static void glamo_mpeg_wait(void)
304 uint16_t status;
306 while ((status = GLAMO_IN_REG(GLAMO_REG_MPEG_DEC_STATUS)) & 0x3000)
307 yield1();
309 if (status & 0x3c0)
310 mp_msg(MSGT_VO, MSGL_ERR, "status = [%s%s%s%s%s%s%s ]\n",
311 (status & 0x000f) ? " ErrorOldFrame" : "",
312 (status & 0x0030) ? " OldFrameIndex" : "",
313 (status & 0x03c0) ? " ErrorThisFrame" : "",
314 (status & 0x0c00) ? " ThisFrameIndex" : "",
315 (status & 0x1000) ? " QueueFull" : "",
316 (status & 0x2000) ? " EngineBusy" : "",
317 (status & 0xc000) ? " DebugInfo" : "");
320 static void glamo_isp_wait(void)
322 uint16_t status;
324 while ((status = GLAMO_IN_REG(GLAMO_REG_ISP_EN1)) &
325 GLAMO_ISP_EN1_FIRE_CAP)
326 yield1();
328 while ((status = GLAMO_IN_REG(GLAMO_REG_ISP_STATUS)) & 1)
329 yield1();
331 bctx.blit_started = 0;
334 struct yuv_buffer {
335 unsigned int y;
336 unsigned int u;
337 unsigned int v;
340 struct mpeg_ctx_s {
341 int width;
342 int height;
344 int cycle;
345 int reg_special;
347 unsigned int buf_in;
348 struct yuv_buffer buf_out[2];
349 unsigned int buf_dc;
350 unsigned int buf_ac;
352 struct glamo_dma_manager *dma;
353 } mctx;
355 static void decode_null(mp_image_t *mpi)
357 uint8_t *vram;
359 vram = (uint8_t *) glamo_fb + mctx.buf_out->y;
360 memcpy(vram, mpi->planes[0], mctx.width * mctx.height);
361 vram = (uint8_t *) glamo_fb + mctx.buf_out->u;
362 memcpy(vram, mpi->planes[1], (mctx.width * mctx.height) >> 2);
363 vram = (uint8_t *) glamo_fb + mctx.buf_out->v;
364 memcpy(vram, mpi->planes[2], (mctx.width * mctx.height) >> 2);
367 static void decode_mpeg(mp_image_t *mpi)
369 mp_mpeg_header_t *picture = (void *) mpi->planes[1];
370 uint32_t glamo_mpeg_header;
371 uint8_t *vram;
372 int cycle = (mctx.cycle ++) & 1;
374 /* Wait until ready */
375 glamo_mpeg_wait();
377 glamo_hw_engine_reset(GLAMO_ENGINE_MPEG);
379 if (mctx.reg_special != picture->vol.time_bits) {
380 /* XXX: for Short Header always equals 8? */
381 mctx.reg_special = picture->vol.time_bits;
382 GLAMO_OUT_REG(GLAMO_REG_MPEG_SPECIAL,
383 (mctx.reg_special << 8) |
384 (mpeg_deblock << 3));
387 glamo_mpeg_header =
388 (picture->vop.start_bits << 0) |
389 (picture->vop.rounding << 3) |
390 ((picture->vop.fcode ?
391 picture->vop.fcode - 1 : 0) << 4) |
392 (picture->vop.coding_type << 7) |
393 (picture->vop.qscale << 8) |
394 (picture->vop.intra_dc_thr << 13) |
395 (cycle << 20) | ((!cycle) << 22);
397 /* Upload the frame.
399 * TODO: use CPU's DMA for this possibly,
400 * TODO: seems that the memcpy size cannot exceed 64K.
401 * TODO: if an error was detected in glamo_mpeg_wait(), the
402 * next frame must be based on the previous frame instead of the
403 * current erroneous one, as a form of recovery. We will need
404 * more buffers for this.
406 vram = (uint8_t *) glamo_fb + mctx.buf_in;
407 st32le(vram, glamo_mpeg_header);
408 memcpy(vram + 4, picture->data_start, picture->data_size);
409 vram += 4 + picture->data_size;
410 /* MPEG terminating header */
411 st32le(vram, 0xb6010000);
413 /* Wait until ready so we can start the blit quickly */
414 if (bctx.blit_started)
415 glamo_isp_wait();
417 /* Decode! */
418 GLAMO_OUT_REG(GLAMO_REG_MPEG_DEC_IN_ADDRL,
419 (mctx.buf_in >> 1) & 0xffff);
420 GLAMO_OUT_REG(GLAMO_REG_MPEG_DEC_IN_ADDRH,
421 ((mctx.buf_in >> 17) & 0x3f) | (cycle << 8));
423 bctx.image_buf = (void *) mctx.buf_out[!cycle].y;
426 #define OUT_ADDR(reg, addr) do { \
427 GLAMO_OUT_REG((reg), ((addr) >> 1) & 0xffff); \
428 GLAMO_OUT_REG((reg) + 2, ((addr) >> 17) & 0x3f); \
429 } while (0)
431 static inline int binlog2(int num)
433 int log;
435 for (log = 0; num > (1 << log); log ++);
437 return log;
440 static int config(uint32_t width, uint32_t height, uint32_t d_width,
441 uint32_t d_height, uint32_t flags, char *title,
442 uint32_t fourcc)
444 int err;
445 int pos;
446 int pitch;
447 int num_mbs;
449 if (!vo_config_count) {
450 err = glamo_os_init(fb_path.str);
451 if (err)
452 return -err;
454 GLAMO_OUT_REG(GLAMO_REG_CLOCK_ISP, 0x0000);
455 GLAMO_OUT_REG(GLAMO_REG_CLOCK_ISP, 0x0000);
456 GLAMO_OUT_REG(GLAMO_REG_CLOCK_MPEG, 0x0000);
457 GLAMO_OUT_REG(GLAMO_REG_CLOCK_MPEG, 0x0000);
458 usleep(10000);
459 glamo_hw_engine_reset(GLAMO_ENGINE_MPEG);
461 glamo_hw_engine_enable(GLAMO_ENGINE_MPEG);
462 mctx.dma = glamo_dma_new(GLAMO_DMA_MODE_MMIO);
465 if (vo_config_count) {
466 glamo_blit_destroy(bctx.blit);
469 mctx.width = width;
470 mctx.height = height;
472 bctx.width = width;
473 bctx.height = height;
474 bctx.out_width = d_width;
475 bctx.out_height = d_height;
476 if (!(flags & VOFLAG_XOVERLAY_SUB_VO))
477 bctx.x = bctx.y = 0;
479 /* TODO: only use GLAMO_VRAM_MPEG for MPEGs */
480 /* Three planes for the output from the decoder and input to BES. */
481 pos = GLAMO_VRAM_MPEG;
482 mctx.buf_out->y = pos;
483 pos += width * height;
484 mctx.buf_out->u = pos;
485 pos += (width * height) >> 2;
486 mctx.buf_out->v = pos;
487 pos += (width * height) >> 2;
489 bctx.image_buf = (void *) mctx.buf_out->y;
490 bctx.blit_started = 0;
492 switch (fourcc) {
493 case IMGFMT_MPEG4:
494 /* TODO: more checks */
495 if (((width > 640 || height > 480) &&
496 (width > 480 && height > 640)) ||
497 ((width | height) & 15)) {
498 mp_msg(MSGT_VO, MSGL_ERR, "bad image size\n");
499 exit(-1);
502 decode_fn = decode_mpeg;
504 mctx.reg_special = -1;
506 /* Three planes for decoder output swap buffer. */
507 mctx.buf_out[1].y = pos;
508 pos += width * height;
509 mctx.buf_out[1].u = pos;
510 pos += (width * height) >> 2;
511 mctx.buf_out[1].v = pos;
512 pos += (width * height) >> 2;
514 /* DC prediction buffer */
515 mctx.buf_dc = pos;
516 pos += (width >> 4) * 6;
518 /* AC prediction buffer */
519 mctx.buf_ac = pos;
520 pos += (width >> 4) * 4 * 12;
522 /* TODO: allocate deblocking framebuffers here */
524 /* MPEG data buffer */
525 mctx.buf_in = pos;
527 bctx.blit = glamo_blit_new(mctx.dma, GLAMO_BLIT_FORMAT_I420);
529 OUT_ADDR(GLAMO_REG_MPEG_DC_ADDRL, mctx.buf_dc);
530 OUT_ADDR(GLAMO_REG_MPEG_AC_ADDRL, mctx.buf_ac);
532 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT0_Y_ADDRL, mctx.buf_out[0].y);
533 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT0_U_ADDRL, mctx.buf_out[0].u);
534 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT0_V_ADDRL, mctx.buf_out[0].v);
535 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT1_Y_ADDRL, mctx.buf_out[1].y);
536 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT1_U_ADDRL, mctx.buf_out[1].u);
537 OUT_ADDR(GLAMO_REG_MPEG_DEC_OUT1_V_ADDRL, mctx.buf_out[1].v);
539 pitch = width;
540 GLAMO_OUT_REG(GLAMO_REG_MPEG_DEC_WIDTH,
541 (width >> 4) | ((pitch >> 1) << 7));
543 /* Yes, UV pitch is left shifted by 8 bits. */
544 pitch = width >> 1;
545 GLAMO_OUT_REG(GLAMO_REG_MPEG_DEC_HEIGHT,
546 (height >> 4) | ((pitch >> 1) << 8));
548 num_mbs = binlog2((width >> 4) * (height >> 4)) + 1;
549 GLAMO_OUT_REG(GLAMO_REG_MPEG_SAFE_1, num_mbs << 11);
550 GLAMO_OUT_REG(GLAMO_REG_MPEG_SAFE_3, 0xf00 | (1 << 15));
551 GLAMO_OUT_REG(GLAMO_REG_MPEG_DEBLK_THRESHOLD,
552 mpeg_db_thr[0] | (mpeg_db_thr[1] << 8));
553 break;
555 case IMGFMT_I420:
556 decode_fn = decode_null;
558 bctx.blit = glamo_blit_new(mctx.dma, GLAMO_BLIT_FORMAT_I420);
559 break;
561 case IMGFMT_YUY2:
562 decode_fn = decode_null;
564 bctx.blit = glamo_blit_new(mctx.dma, GLAMO_BLIT_FORMAT_YUY2);
565 break;
567 default:
568 return EINVAL;
571 glamo_blit_rotate(bctx.blit, rotate);
573 return 0;
576 static void uninit(void) {
577 if (!vo_config_count)
578 return;
580 glamo_blit_destroy(bctx.blit);
581 glamo_dma_destroy(mctx.dma);
583 glamo_hw_engine_disable(GLAMO_ENGINE_MPEG);
584 glamo_os_finish();