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.
29 #include <sys/ioctl.h>
31 #include <sys/types.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"
44 #include "video_out.h"
45 #include "video_out_internal.h"
48 #include "libmpdemux/mpeg4_hdr.h"
49 #include "subopt-helper.h"
52 #include "x11_common.h"
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)
72 /* MPEG1 stream for our MPEG engine. */
74 /* Decoded frames to blit onto the framebuffer using the 2D engine
75 * and bypassing MPEG block. */
78 case IMGFMT_RGB16
: /* TODO */
79 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
|
80 VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_FLIP
;
86 static struct blit_ctx_s
{
87 struct glamo_blit_manager
*blit
;
98 static void (*decode_fn
)(mp_image_t
*mpi
);
100 static int draw_image(mp_image_t
*mpi
)
102 struct glamo_blit_image
*image
;
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
);
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
)
150 static int draw_frame(uint8_t *src
[])
155 static void check_events(void)
158 int e
= vo_check_events();
160 mp_msg(MSGT_VO
, MSGL_INFO
, "got event %x\n", e
);
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
175 static int glamo_window(mp_win_t
*win
)
179 bctx
.out_width
= win
->w
;
180 bctx
.out_height
= win
->h
;
185 static int control(uint32_t request
, void *data
)
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
:
199 case VOCTRL_XOVERLAY_SET_WIN
:
200 return glamo_window((mp_win_t
*) data
);
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 },
223 static int preinit(const char *arg
)
225 uint16_t dev_id
, rev_id
;
228 err
= subopt_parse(arg
, glamo_subopts
);
232 err
= glamo_os_init(fb_path
.str
);
236 mpeg_deblock
= mpeg_db_thr
[0] || mpeg_db_thr
[1];
238 switch (angle
% 360) {
240 rotate
= GLAMO_BLIT_ROTATION_0
;
243 rotate
= GLAMO_BLIT_ROTATION_90
;
246 rotate
= GLAMO_BLIT_ROTATION_180
;
249 rotate
= GLAMO_BLIT_ROTATION_270
;
252 mp_msg(MSGT_VO
, MSGL_ERR
,
253 "unsupported angle: %i deg\n", angle
);
257 dev_id
= GLAMO_IN_REG(GLAMO_REG_DEVICE_ID
);
258 rev_id
= GLAMO_IN_REG(GLAMO_REG_REVISION_ID
);
266 dev_id
= 0x3000 | (dev_id
>> 4);
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
);
278 void inline st32le(void *ptr
, uint32_t value
)
280 #if __BYTE_ORDER == __LITTLE_ENDIAN
282 memcpy(ptr
, &value
, 4);
284 *(uint32_t *) ptr
= value
;
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;
294 static void yield1(void)
296 const struct timespec req
= { 0, 500000 };
302 static void glamo_mpeg_wait(void)
306 while ((status
= GLAMO_IN_REG(GLAMO_REG_MPEG_DEC_STATUS
)) & 0x3000)
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)
324 while ((status
= GLAMO_IN_REG(GLAMO_REG_ISP_EN1
)) &
325 GLAMO_ISP_EN1_FIRE_CAP
)
328 while ((status
= GLAMO_IN_REG(GLAMO_REG_ISP_STATUS
)) & 1)
331 bctx
.blit_started
= 0;
348 struct yuv_buffer buf_out
[2];
352 struct glamo_dma_manager
*dma
;
355 static void decode_null(mp_image_t
*mpi
)
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
;
372 int cycle
= (mctx
.cycle
++) & 1;
374 /* Wait until ready */
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));
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);
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
)
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); \
431 static inline int binlog2(int num
)
435 for (log
= 0; num
> (1 << log
); 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
,
449 if (!vo_config_count
) {
450 err
= glamo_os_init(fb_path
.str
);
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);
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
);
470 mctx
.height
= height
;
473 bctx
.height
= height
;
474 bctx
.out_width
= d_width
;
475 bctx
.out_height
= d_height
;
476 if (!(flags
& VOFLAG_XOVERLAY_SUB_VO
))
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;
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");
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 */
516 pos
+= (width
>> 4) * 6;
518 /* AC prediction buffer */
520 pos
+= (width
>> 4) * 4 * 12;
522 /* TODO: allocate deblocking framebuffers here */
524 /* MPEG data buffer */
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
);
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. */
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));
556 decode_fn
= decode_null
;
558 bctx
.blit
= glamo_blit_new(mctx
.dma
, GLAMO_BLIT_FORMAT_I420
);
562 decode_fn
= decode_null
;
564 bctx
.blit
= glamo_blit_new(mctx
.dma
, GLAMO_BLIT_FORMAT_YUY2
);
571 glamo_blit_rotate(bctx
.blit
, rotate
);
576 static void uninit(void) {
577 if (!vo_config_count
)
580 glamo_blit_destroy(bctx
.blit
);
581 glamo_dma_destroy(mctx
.dma
);
583 glamo_hw_engine_disable(GLAMO_ENGINE_MPEG
);