diff --git a/mmal_8.patch b/mmal_10.patch similarity index 86% rename from mmal_8.patch rename to mmal_10.patch index 8744fcb..6ed981e 100644 --- a/mmal_8.patch +++ b/mmal_10.patch @@ -1,6 +1,6 @@ --- a/configure.ac +++ b/configure.ac -@@ -3419,6 +3419,9 @@ +@@ -3420,6 +3420,9 @@ AC_ARG_ENABLE(mmal, AS_HELP_STRING([--enable-mmal], [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)])) @@ -10,7 +10,7 @@ if test "${enable_mmal}" != "no"; then VLC_SAVE_FLAGS LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif" -@@ -3429,7 +3432,7 @@ +@@ -3430,7 +3433,7 @@ VLC_ADD_PLUGIN([mmal]) VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ]) VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ]) @@ -19,7 +19,7 @@ AS_IF([test "${enable_mmal}" = "yes"], [ AC_MSG_ERROR([Cannot find bcm library...]) ], [ AC_MSG_WARN([Cannot find bcm library...]) ]) -@@ -3441,6 +3444,7 @@ +@@ -3442,6 +3445,7 @@ VLC_RESTORE_FLAGS fi AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) @@ -29,19 +29,20 @@ dnl evas plugin --- a/include/vlc_fourcc.h +++ b/include/vlc_fourcc.h -@@ -365,6 +365,9 @@ +@@ -365,6 +365,10 @@ /* Broadcom MMAL opaque buffer type */ #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L') +#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8') +#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0') +#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0') ++#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B') /* DXVA2 opaque video surface for use with D3D9 */ #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */ --- a/modules/hw/mmal/Makefile.am +++ b/modules/hw/mmal/Makefile.am -@@ -1,23 +1,39 @@ +@@ -1,23 +1,46 @@ include $(top_srcdir)/modules/common.am mmaldir = $(pluginsdir)/mmal @@ -58,7 +59,8 @@ mmal_LTLIBRARIES = libmmal_vout_plugin.la -libmmal_codec_plugin_la_SOURCES = codec.c -+libmmal_codec_plugin_la_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h ++libmmal_codec_plugin_la_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h\ ++ mmal_cma.c mmal_cma.h libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS) libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS) libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal) @@ -77,6 +79,12 @@ +libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal) +mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la + ++libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c mmal_cma.h mmal_picture.h ++libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS) ++libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS) ++libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal) ++mmal_LTLIBRARIES += libmmal_converter_plugin.la ++ +if HAVE_MMAL_AVCODEC +libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_picture.c mmal_picture.h +libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) @@ -88,7 +96,7 @@ + --- /dev/null +++ b/modules/hw/mmal/blend_rgba_neon.S -@@ -0,0 +1,200 @@ +@@ -0,0 +1,197 @@ + .syntax unified + .arm +// .thumb @@ -142,7 +150,6 @@ + vld4.8 {d16, d17, d18, d19}, [r1] + +1: -+ @ Alpha ends up in d19 + vmull.u8 q15, \sA, d7 + + vld4.8 {d20, d21, d22, d23}, [r0] @@ -189,26 +196,27 @@ + mov r2, r0 + bcc 1f + vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]! -+ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! ++ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r2]! + vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]! -+ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! ++ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r2]! + vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]! -+ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]! ++ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r2]! + vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]! -+ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]! ++ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r2]! +1: + bpl 1f + vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]! -+ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]! ++ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r2]! + vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]! -+ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! ++ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r2]! +1: + tst r3, #1 + beq 1f + vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]! -+ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! ++ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r2]! +1: -+ @ Alpha ends up in d19 ++ @ Set conditions for later ++ lsls r2, r3, #30 @ b2 -> C, b1 -> N + + vmull.u8 q15, \sA, d7 + vsra.u16 q15, q15, #8 @@ -232,9 +240,6 @@ + vrshrn.u16 \dB, q14, #8 + vmov.u8 \dA, #0xff + -+ mov r0, r2 -+ lsls r2, r3, #30 @ b2 -> C, b1 -> N -+ mov r2, r0 + bcc 1f + vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! + vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! @@ -246,9 +251,9 @@ + vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! +1: + tst r3, #1 -+ beq 1f ++ bxeq lr + vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! -+1: ++ + bx lr + +.endm @@ -508,15 +513,20 @@ #include #include -@@ -38,255 +40,393 @@ +@@ -37,256 +39,383 @@ + #include #include ++#include ++ ++#include "mmal_cma.h" #include "mmal_picture.h" + +#include "subpic.h" +#include "blend_rgba_neon.h" + +#define TRACE_ALL 0 - ++ /* * This seems to be a bit high, but reducing it causes instabilities */ @@ -554,7 +564,7 @@ - add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) - set_callbacks(OpenDecoder, CloseDecoder) -vlc_module_end() - +- -struct decoder_sys_t { - bool opaque; +typedef struct decoder_sys_t @@ -574,6 +584,8 @@ + bool b_flushed; + ++ vcsm_init_type_t vcsm_init_type; ++ + // Lock to avoid pic update & allocate happenening simultainiously + // * We should be able to arrange life s.t. this isn't needed + // but while we are confused apply belt & braces @@ -583,22 +595,8 @@ - int output_in_transit; - int input_in_transit; atomic_bool started; +-}; +} decoder_sys_t; -+ -+ -+typedef struct supported_mmal_enc_s { -+ struct { -+ MMAL_PARAMETER_HEADER_T header; -+ MMAL_FOURCC_T encodings[64]; -+ } supported; -+ int n; -+} supported_mmal_enc_t; -+ -+static supported_mmal_enc_t supported_mmal_enc = -+{ -+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, -+ -1 - }; -/* Utilities */ -static int change_output_format(decoder_t *dec); @@ -613,46 +611,46 @@ -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -+static inline char safe_char(const unsigned int c0) -+{ -+ const unsigned int c = c0 & 0xff; -+ return c > ' ' && c < 0x7f ? c : '.'; -+} -static int OpenDecoder(decoder_t *dec) -+static const char * str_fourcc(char * buf, unsigned int fcc) - { +-{ - int ret = VLC_SUCCESS; - decoder_sys_t *sys; - MMAL_PARAMETER_UINT32_T extra_buffers; - MMAL_STATUS_T status; -+ if (fcc == 0) -+ return "----"; -+ buf[0] = safe_char(fcc >> 0); -+ buf[1] = safe_char(fcc >> 8); -+ buf[2] = safe_char(fcc >> 16); -+ buf[3] = safe_char(fcc >> 24); -+ buf[4] = 0; -+ return buf; ++typedef struct supported_mmal_enc_s { ++ struct { ++ MMAL_PARAMETER_HEADER_T header; ++ MMAL_FOURCC_T encodings[64]; ++ } supported; ++ int n; ++} supported_mmal_enc_t; ++ ++#define SUPPORTED_MMAL_ENC_INIT \ ++{ \ ++ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \ ++ -1 \ +} - if (dec->fmt_in.i_codec != VLC_CODEC_MPGV && - dec->fmt_in.i_codec != VLC_CODEC_H264) - return VLC_EGENERIC; -+static bool is_enc_supported(const MMAL_FOURCC_T fcc) -+{ -+ int i; ++static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT; - sys = calloc(1, sizeof(decoder_sys_t)); - if (!sys) { - ret = VLC_ENOMEM; - goto out; ++static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc) ++{ ++ int i; ++ + if (fcc == 0) + return false; -+ if (supported_mmal_enc.n == -1) ++ if (support->n == -1) + return true; // Unknown - say OK -+ for (i = 0; i < supported_mmal_enc.n; ++i) { -+ if (supported_mmal_enc.supported.encodings[i] == fcc) ++ for (i = 0; i < support->n; ++i) { ++ if (support->supported.encodings[i] == fcc) + return true; } - dec->p_sys = sys; @@ -661,15 +659,15 @@ - sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME); - bcm_host_init(); -+static bool set_and_test_enc_supported(MMAL_PORT_T * port, const MMAL_FOURCC_T fcc) ++static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc) +{ -+ if (supported_mmal_enc.n >= 0) ++ if (support->n >= 0) + /* already done */; -+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&supported_mmal_enc.supported) != MMAL_SUCCESS) -+ supported_mmal_enc.n = 0; ++ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS) ++ support->n = 0; + else -+ supported_mmal_enc.n = (supported_mmal_enc.supported.header.size - sizeof(supported_mmal_enc.supported.header)) / -+ sizeof(supported_mmal_enc.supported.encodings[0]); ++ support->n = (support->supported.header.size - sizeof(support->supported.header)) / ++ sizeof(support->supported.encodings[0]); - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); - if (status != MMAL_SUCCESS) { @@ -678,7 +676,7 @@ - ret = VLC_EGENERIC; - goto out; - } -+ return is_enc_supported(fcc); ++ return is_enc_supported(support, fcc); +} - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; @@ -1071,7 +1069,7 @@ if (atomic_load(&sys->started)) { mmal_format_full_copy(sys->output->format, sys->output_format); status = mmal_port_format_commit(sys->output); -@@ -300,7 +440,9 @@ +@@ -300,7 +429,9 @@ } port_reset: @@ -1081,7 +1079,7 @@ status = mmal_port_disable(sys->output); if (status != MMAL_SUCCESS) { msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)", -@@ -318,18 +460,10 @@ +@@ -318,18 +449,10 @@ goto out; } @@ -1102,7 +1100,7 @@ if (status != MMAL_SUCCESS) { msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)", status, mmal_status_to_string(status)); -@@ -338,25 +472,14 @@ +@@ -338,25 +461,14 @@ } if (!atomic_load(&sys->started)) { @@ -1131,7 +1129,7 @@ } apply_fmt: -@@ -382,12 +505,19 @@ +@@ -382,12 +494,19 @@ sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive); sys->b_top_field_first = sys->b_progressive ? true : (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst); @@ -1151,7 +1149,7 @@ out: mmal_format_free(sys->output_format); sys->output_format = NULL; -@@ -395,144 +525,85 @@ +@@ -395,144 +514,85 @@ return ret; } @@ -1201,7 +1199,7 @@ - ret = VLC_EGENERIC; - goto err; - } -- + - if (!sys->opaque) - buffer->data = picture->p[0].p_pixels; - } else { @@ -1215,7 +1213,7 @@ - } - buffer->user_data = picture; - buffer->cmd = 0; - +- - status = mmal_port_send_buffer(sys->output, buffer); + status = mmal_port_format_commit(sys->input); if (status != MMAL_SUCCESS) { @@ -1302,6 +1300,10 @@ - MMAL_BUFFER_HEADER_T *buffer; - MMAL_STATUS_T status; + decoder_sys_t *const sys = dec->p_sys; ++ ++#if TRACE_ALL ++ msg_Dbg(dec, "%s: <<<", __func__); ++#endif - msg_Dbg(dec, "Flushing decoder ports..."); - mmal_port_flush(sys->output); @@ -1310,10 +1312,6 @@ - while (atomic_load(&sys->output_in_transit) || - atomic_load(&sys->input_in_transit)) - vlc_sem_wait(&sys->sem); -+#if TRACE_ALL -+ msg_Dbg(dec, "%s: <<<", __func__); -+#endif -+ + if (!sys->b_flushed) { + mmal_port_disable(sys->input); + mmal_port_disable(sys->output); @@ -1349,7 +1347,7 @@ /* * Configure output port if necessary */ -@@ -541,18 +612,50 @@ +@@ -541,18 +601,50 @@ msg_Err(dec, "Failed to change output port format"); } @@ -1403,7 +1401,7 @@ if (atomic_load(&sys->started)) fill_output_port(dec); -@@ -563,18 +666,21 @@ +@@ -563,18 +655,21 @@ if (block->i_flags & BLOCK_FLAG_CORRUPTED) flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; @@ -1430,7 +1428,7 @@ len = block->i_buffer; if (len > buffer->alloc_size) -@@ -590,89 +696,1443 @@ +@@ -590,89 +685,1549 @@ } buffer->flags = flags; @@ -1499,6 +1497,8 @@ + + hw_mmal_port_pool_ref_release(sys->ppr, false); + ++ cma_vcsm_exit(sys->vcsm_init_type); ++ + vlc_mutex_destroy(&sys->pic_lock); + free(sys); + @@ -1527,7 +1527,7 @@ + } +#endif + -+ if (!is_enc_supported(in_fcc)) ++ if (!is_enc_supported(&supported_decode_in_enc, in_fcc)) + return VLC_EGENERIC; + + sys = calloc(1, sizeof(decoder_sys_t)); @@ -1540,6 +1540,12 @@ + + bcm_host_init(); + ++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { ++ msg_Err(dec, "VCSM init failed"); ++ goto fail; ++ } ++ msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type)); ++ + sys->err_stream = MMAL_SUCCESS; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); @@ -1555,7 +1561,7 @@ + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; + sys->input->format->encoding = in_fcc; + -+ if (!set_and_test_enc_supported(sys->input, in_fcc)) { ++ if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) { +#if TRACE_ALL + char cbuf[5]; + msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc)); @@ -1696,6 +1702,8 @@ + MMAL_POOL_T *out_pool; // Free output buffers + MMAL_POOL_T *in_pool; // Input pool to get BH for replication + ++ cma_pool_fixed_t * cma_out_pool; ++ + subpic_reg_stash_t subs[SUBS_MAX]; + + pic_fifo_t ret_pics; @@ -1709,6 +1717,7 @@ + MMAL_STATUS_T err_stream; + int in_count; + ++ bool is_cma; + bool is_sliced; + bool out_fmt_set; + bool latency_set; @@ -1733,18 +1742,17 @@ + unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3; + MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; + -+ if (bpp < 1 || bpp > 4) -+ return MMAL_EINVAL; -+ + es_fmt->type = MMAL_ES_TYPE_VIDEO; + es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format); + es_fmt->encoding_variant = 0; + + // Fill in crop etc. + vlc_to_mmal_video_fmt(es_fmt, &pic->format); -+ // Override width / height with strides -+ v_fmt->width = pic->p[0].i_pitch / bpp; -+ v_fmt->height = pic->p[0].i_lines; ++ // Override width / height with strides if appropriate ++ if (bpp != 0) { ++ v_fmt->width = pic->p[0].i_pitch / bpp; ++ v_fmt->height = pic->p[0].i_lines; ++ } + return MMAL_SUCCESS; +} + @@ -1766,6 +1774,20 @@ +{ + MMAL_STATUS_T err = MMAL_SUCCESS; + ++ if (sys->is_cma) ++ { ++ if (sys->cma_out_pool == NULL && ++ (sys->cma_out_pool = cma_buf_pool_new()) == NULL) ++ { ++ msg_Err(p_filter, "Failed to alloc cma buf pool"); ++ return MMAL_ENOMEM; ++ } ++ } ++ else ++ { ++ cma_buf_pool_deletez(&sys->cma_out_pool); ++ } ++ + if (!sys->output->is_enabled && + (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS) + { @@ -1837,7 +1859,10 @@ } -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) -+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) ++static const uint8_t shift_00[] = {0,0,0,0}; ++static const uint8_t shift_01[] = {0,1,1,1}; ++ ++static int cma_pic_set_data(filter_t * const p_filter, picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf) { - decoder_t *dec = (decoder_t *)port->userdata; - decoder_sys_t *sys = dec->p_sys; @@ -1859,6 +1884,61 @@ - buffer->alloc_size = 0; - buffer->data = NULL; - mmal_buffer_header_release(buffer); +- } +- } +- atomic_fetch_sub(&sys->output_in_transit, 1); +- vlc_sem_post(&sys->sem); +- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { +- fmt = mmal_event_format_changed_get(buffer); ++ filter_sys_t *const sys = p_filter->p_sys; ++ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &sys->output->format->es->video; ++ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = &buf->type->video; ++ ++ uint8_t * const data = cma_buf_pic_addr(pic); ++ if (data == NULL) { ++ return VLC_ENOMEM; ++ } ++ ++ const uint8_t * ws = shift_00; ++ const uint8_t * hs = shift_00; ++ int pb = 1; ++ ++ switch (p_filter->fmt_out.video.i_chroma) ++ { ++ case VLC_CODEC_MMAL_ZC_RGB32: ++ pb = 4; ++ break; + +- format = mmal_format_alloc(); +- mmal_format_full_copy(format, fmt->format); ++ case VLC_CODEC_MMAL_ZC_I420: ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ hs = shift_01; ++ break; + +- if (sys->opaque) +- format->encoding = MMAL_ENCODING_OPAQUE; ++ default: ++ msg_Err(p_filter, "%s: Unexpected format", __func__); ++ return VLC_EGENERIC; ++ } ++ ++ pic->i_planes = buf_vid->planes; ++ for (unsigned int i = 0; i != buf_vid->planes; ++i) { ++ pic->p[i] = (plane_t){ ++ .p_pixels = data + buf_vid->offset[i], ++ .i_lines = mm_fmt->height >> hs[i], ++ .i_pitch = buf_vid->pitch[i], ++ .i_pixel_pitch = pb, ++ .i_visible_lines = mm_fmt->crop.height >> hs[i], ++ .i_visible_pitch = mm_fmt->crop.width >> ws[i] ++ }; ++ } ++ return VLC_SUCCESS; ++} ++ ++static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) ++{ + filter_t * const p_filter = (filter_t *)port->userdata; + filter_sys_t * const sys = p_filter->p_sys; + @@ -1884,6 +1964,11 @@ + { + buf_to_pic_copy_props(pic, buf); + ++ if (sys->is_cma) { ++ if (cma_pic_set_data(p_filter, pic, buf) != VLC_SUCCESS) ++ msg_Err(p_filter, "Failed to set data"); ++ } ++ +// draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines); +#if DEBUG_SQUARES + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000); @@ -1949,7 +2034,7 @@ + if (src_stride == dst_stride) { + if (copy_n != 0) + memcpy(dst, src, src_stride * copy_n); - } ++ } + else { + unsigned int i; + for (i = 0; i != copy_n; ++i) { @@ -1959,18 +2044,10 @@ + } + } + sys->slice.line += scale_n; - } -- atomic_fetch_sub(&sys->output_in_transit, 1); -- vlc_sem_post(&sys->sem); -- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { -- fmt = mmal_event_format_changed_get(buffer); - -- format = mmal_format_alloc(); -- mmal_format_full_copy(format, fmt->format); ++ } ++ + if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) { - -- if (sys->opaque) -- format->encoding = MMAL_ENCODING_OPAQUE; ++ + if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) { + // Stuff doesn't add up... + msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags); @@ -1988,7 +2065,8 @@ + } + } + } -+ + +- sys->output_format = format; + // Put back + buf->user_data = NULL; // Zap here to make sure we can't reuse later + mmal_buffer_header_reset(buf); @@ -2026,6 +2104,8 @@ + if (sys->output != NULL && sys->output->is_enabled) + mmal_port_disable(sys->output); + ++ cma_buf_pool_deletez(&sys->cma_out_pool); ++ + // Free up anything we may have already lying around + // Don't need lock as the above disables should have prevented anything + // happening in the background @@ -2101,7 +2181,7 @@ + if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS) + { + char cbuf[5]; -+ msg_Err(p_filter, "Bad format desc: %s, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic->format.i_bits_per_pixel); ++ msg_Err(p_filter, "Bad format desc: %s, pic=%p, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic, pic->format.i_bits_per_pixel); + return status; + } + @@ -2120,11 +2200,11 @@ + + sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended); + sys->output->buffer_size = sys->output->buffer_size_recommended; -+ + +- mmal_buffer_header_release(buffer); + if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS) + return status; - -- sys->output_format = format; ++ + return MMAL_SUCCESS; +} + @@ -2167,8 +2247,12 @@ + + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { + int rv; -+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), p_pic, sub_no, sys->subs + sub_no, -+ &sys->output->format->es->video.crop, frame_seq)) == 0) ++ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), ++ hw_mmal_pic_sub_buf_get(p_pic, sub_no), ++ sys->subs + sub_no, ++ &p_pic->format, ++ &sys->output->format->es->video.crop, ++ frame_seq)) == 0) + break; + else if (rv < 0) + goto fail; @@ -2201,7 +2285,6 @@ + goto fail; + } + -+ msg_Dbg(p_filter, "Outpool: zc=%d, num=%d, size=%d", sys->is_sliced, sys->output->buffer_num, sys->output->buffer_size); + sys->out_pool = sys->is_sliced ? + mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) : + mmal_pool_create(sys->output->buffer_num, 0); @@ -2291,13 +2374,35 @@ + + mmal_buffer_header_reset(out_buf); + out_buf->user_data = out_pic; -+ out_buf->data = out_pic->p[0].p_pixels; -+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines; -+ //**** stride ???? ++ ++ if (sys->is_cma) { ++ int rv; ++ if ((rv = cma_buf_pic_attach(sys->cma_out_pool, out_pic, sys->output->buffer_size)) != VLC_SUCCESS) ++ { ++ char dbuf0[5]; ++ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", ++ str_fourcc(dbuf0, out_pic->format.i_chroma), ++ rv); ++ goto fail; ++ } ++ const unsigned int vc_h = cma_buf_pic_vc_handle(out_pic); ++ if (vc_h == 0) ++ { ++ msg_Err(p_filter, "Pic has no vc handle"); ++ goto fail; ++ } ++ out_buf->data = (uint8_t *)vc_h; ++ out_buf->alloc_size = sys->output->buffer_size; ++ } ++ else { ++ out_buf->data = out_pic->p[0].p_pixels; ++ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines; ++ //**** stride ???? ++ } + +#if TRACE_ALL -+ msg_Dbg(p_filter, "Out buf send: pic=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld", -+ p_pic, out_buf->user_data, out_buf->flags, ++ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", ++ p_pic, out_buf->data, out_buf->user_data, out_buf->flags, + out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); +#endif + @@ -2365,8 +2470,7 @@ +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); +#endif - -- mmal_buffer_header_release(buffer); ++ + return ret_pics; + +stream_fail: @@ -2488,6 +2592,8 @@ + use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME); + +retry: ++ // ** Make more generic by checking supported encs ++ // + // Must use ISP - HVS can't do this, nor can resizer + if (enc_in == MMAL_ENCODING_YUVUV64_10) { + // If resizer selected then just give up @@ -2496,6 +2602,11 @@ + // otherwise downgrade HVS to ISP + use_isp = true; + } ++ // HVS can't do I420 ++ if (enc_out == MMAL_ENCODING_I420) { ++ use_isp = true; ++ } ++ + + if (use_resizer) { + // use resizer overrides use_isp @@ -2543,6 +2654,7 @@ + pic_fifo_init(&sys->slice.pics); + + sys->in_port_cb_fn = conv_input_port_cb; ++ + if (use_resizer) { + sys->resizer_type = FILTER_RESIZER_RESIZER; + sys->is_sliced = true; @@ -2561,6 +2673,7 @@ + sys->component_name = MMAL_COMPONENT_HVS; + sys->out_port_cb_fn = conv_output_port_cb; + } ++ sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma); + + status = mmal_component_create(sys->component_name, &sys->component); + if (status != MMAL_SUCCESS) { @@ -2573,7 +2686,7 @@ + msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", + MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); + goto fail; - } ++ } + sys->output = sys->component->output[0]; + sys->input = sys->component->input[0]; + @@ -2583,7 +2696,7 @@ + msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)", + sys->component->control->name, status, mmal_status_to_string(status)); + goto fail; -+ } + } + + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; + sys->input->format->type = MMAL_ES_TYPE_VIDEO; @@ -2606,7 +2719,7 @@ + if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) + goto fail; + -+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced); ++ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma); + + status = mmal_component_enable(sys->component); + if (status != MMAL_SUCCESS) { @@ -2685,24 +2798,15 @@ + else + { + // cast away src const so we can ref it -+ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, dst, ++ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, ++ vis_mmal_rect(&dst->format), ++ x_offset, y_offset, ++ alpha, + dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst)); + if (buf == NULL) { + msg_Err(p_filter, "Failed to allocate vzc buffer for subpic"); + return; + } -+ MMAL_DISPLAYREGION_T * const reg = hw_mmal_vzc_buf_region(buf); -+ -+ reg->set |= -+ MMAL_DISPLAY_SET_ALPHA | MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_DEST_RECT; -+ -+ reg->fullscreen = 0; -+ -+ reg->alpha = (uint32_t)(alpha & 0xff) | (1U << 31); -+ -+ hw_mmal_vzc_buf_set_dest_rect(buf, x_offset, y_offset, src->format.i_visible_width, src->format.i_visible_height); -+ -+ reg->dest_rect = (MMAL_RECT_T){0, 0, 0, 0}; + + hw_mmal_pic_sub_buf_add(dst, buf); + @@ -2767,7 +2871,7 @@ + + hw_mmal_vzc_pool_release(sys->vzc); + free(sys); -+} + } + +// --------------------------------------------------------------------------- + @@ -2825,7 +2929,7 @@ +static void CloseBlendNeon(vlc_object_t *object) +{ + VLC_UNUSED(object); - } ++} + +static int OpenBlendNeon(vlc_object_t *object) +{ @@ -2860,7 +2964,9 @@ + } + + if (blend_fn == (blend_neon_fn *)0) ++ { + return VLC_EGENERIC; ++ } + + p_filter->p_sys = (void *)blend_fn; + p_filter->pf_video_blend = FilterBlendNeon; @@ -2925,87 +3031,538 @@ +vlc_module_end() + + ---- a/modules/hw/mmal/deinterlace.c -+++ b/modules/hw/mmal/deinterlace.c -@@ -26,11 +26,12 @@ - #include "config.h" - #endif - --#include -+#include +--- /dev/null ++++ b/modules/hw/mmal/converter_mmal.c +@@ -0,0 +1,448 @@ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif + - #include -+#include - #include - #include --#include - - #include "mmal_picture.h" - -@@ -41,466 +42,569 @@ - - #define MIN_NUM_BUFFERS_IN_TRANSIT 2 - --#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu" --#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.") --#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.") -+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu" -+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.") -+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.") - --static int Open(filter_t *filter); --static void Close(filter_t *filter); -+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv" -+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace") -+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace") - --vlc_module_begin() -- set_shortname(N_("MMAL deinterlace")) -- set_description(N_("MMAL-based deinterlace filter plugin")) -- set_capability("video filter", 0) -- set_category(CAT_VIDEO) -- set_subcategory(SUBCAT_VIDEO_VFILTER) -- set_callbacks(Open, Close) -- add_shortcut("deinterlace") -- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT, -- MMAL_DEINTERLACE_QPU_LONGTEXT, true); --vlc_module_end() -+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast" -+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace") -+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace") ++#include ++#include ++#include ++#include + -+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none" -+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace") -+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\ -+ "This is the default for > SD if < 96M gpu-mem") ++#include + -+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate" -+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate") -+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input") ++#include ++#include + -+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate" -+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate") -+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input") - --struct filter_sys_t { ++#include ++#include ++#include ++#include ++#include + -+typedef struct filter_sys_t -+{ - MMAL_COMPONENT_T *component; - MMAL_PORT_T *input; - MMAL_PORT_T *output; -+ MMAL_POOL_T *in_pool; -+ hw_mmal_port_pool_ref_t *out_ppr; - -- MMAL_QUEUE_T *filtered_pictures; -- vlc_sem_t sem; -+ MMAL_QUEUE_T * out_q; - -- atomic_bool started; -+ bool half_rate; -+ bool use_qpu; -+ bool use_fast; -+ bool use_passthrough; ++#include "mmal_cma.h" ++ ++#include "../../video_output/opengl/converter.h" ++ ++#include "mmal_picture.h" ++ ++#include ++ ++#define OPT_SAND 0 ++#define OPT_I420 1 ++#define OPT_RGB32 0 ++ ++ ++#if OPT_SAND ++#define FMT_IN VLC_CODEC_MMAL_ZC_SAND8 ++#elif OPT_I420 ++#define FMT_IN VLC_CODEC_MMAL_ZC_I420 ++#elif OPT_RGB32 ++#define FMT_IN VLC_CODEC_MMAL_ZC_RGB32 ++#elif ++#error Missing input format ++#endif ++ ++#define TRACE_ALL 0 ++ ++typedef struct mmal_gl_converter_s ++{ ++ EGLint drm_fourcc; ++ vcsm_init_type_t vcsm_init_type; ++ struct cma_pic_context_s * last_ctx_ref; ++ ++ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; ++} mmal_gl_converter_t; ++ ++ ++static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt) ++{ ++ // Converting to mmal selects the right RGB32 varient ++ switch(vlc_to_mmal_video_fourcc(fmt)) ++ { ++ case MMAL_ENCODING_I420: ++ return MMAL_FOURCC('Y','U','1','2'); ++ case MMAL_ENCODING_YV12: ++ return MMAL_FOURCC('Y','V','1','2'); ++ case MMAL_ENCODING_I422: ++ return MMAL_FOURCC('Y','U','1','6'); ++ case MMAL_ENCODING_YUVUV128: ++ case MMAL_ENCODING_NV12: ++ return MMAL_FOURCC('N','V','1','2'); ++ case MMAL_ENCODING_NV21: ++ return MMAL_FOURCC('N','V','2','1'); ++ case MMAL_ENCODING_RGB16: ++ return MMAL_FOURCC('R','G','1','6'); ++ case MMAL_ENCODING_RGB24: ++ return MMAL_FOURCC('B','G','2','4'); ++ case MMAL_ENCODING_BGR24: ++ return MMAL_FOURCC('R','G','2','4'); ++ case MMAL_ENCODING_BGR32: ++ case MMAL_ENCODING_BGRA: ++ return MMAL_FOURCC('X','R','2','4'); ++ case MMAL_ENCODING_RGB32: ++ case MMAL_ENCODING_RGBA: ++ return MMAL_FOURCC('X','B','2','4'); ++ default: ++ break; ++ } ++ return 0; ++} ++ ++typedef struct tex_context_s { ++ picture_context_t cmn; ++ GLuint texture; ++ ++ PFNGLDELETETEXTURESPROC DeleteTextures; // Copy fn pointer so we don't need tc on delete ++} tex_context_t; ++ ++static void tex_context_delete(tex_context_t * const tex) ++{ ++ tex->DeleteTextures(1, &tex->texture); ++ ++ free(tex); ++} ++ ++static void tex_context_destroy(picture_context_t * pic_ctx) ++{ ++ tex_context_delete((tex_context_t *)pic_ctx); ++} ++ ++static picture_context_t * tex_context_copy(picture_context_t * pic_ctx) ++{ ++ return pic_ctx; ++} ++ ++static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic) ++{ ++ mmal_gl_converter_t * const sys = tc->priv; ++ ++ tex_context_t * tex = (tex_context_t *)cma_buf_pic_context2(pic); ++ if (tex != NULL) ++ return tex; ++ ++ if ((tex = malloc(sizeof(*tex))) == NULL) ++ return NULL; ++ ++ *tex = (tex_context_t){ ++ .cmn = { ++ .destroy = tex_context_destroy, ++ .copy = tex_context_copy ++ }, ++ .texture = 0, ++ .DeleteTextures = tc->vt->DeleteTextures ++ }; ++ ++ { ++ EGLint attribs[30]; ++ EGLint * a = attribs; ++ const int fd = cma_buf_pic_fd(pic); ++ uint8_t * base_addr = cma_buf_pic_addr(pic); ++ ++ if (pic->i_planes >= 4 || pic->i_planes <= 0) ++ { ++ msg_Err(tc, "%s: Bad planes", __func__); ++ goto fail; ++ } ++ ++ *a++ = EGL_WIDTH; ++ *a++ = pic->format.i_visible_width; ++ *a++ = EGL_HEIGHT; ++ *a++ = pic->format.i_visible_height; ++ *a++ = EGL_LINUX_DRM_FOURCC_EXT; ++ *a++ = sys->drm_fourcc; ++ ++ if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8) ++ { ++ // Sand is its own very special bunny :-( ++ static const EGLint attnames[] = { ++ EGL_DMA_BUF_PLANE0_FD_EXT, ++ EGL_DMA_BUF_PLANE0_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE0_PITCH_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE1_FD_EXT, ++ EGL_DMA_BUF_PLANE1_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE1_PITCH_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT ++ }; ++ ++ const EGLint * n = attnames; ++ ++ for (int i = 0; i < pic->i_planes; ++i) ++ { ++ const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7); ++ ++ *a++ = *n++; ++ *a++ = fd; ++ *a++ = *n++; ++ *a++ = pic->p[i].p_pixels - base_addr; ++ *a++ = *n++; ++ *a++ = pic->format.i_width; ++ *a++ = *n++; ++ *a++ = (EGLint)(mod >> 32); ++ *a++ = *n++; ++ *a++ = (EGLint)(mod & 0xffffffff); ++ } ++ } ++ else ++ { ++ static const EGLint attnames[] = { ++ EGL_DMA_BUF_PLANE0_FD_EXT, ++ EGL_DMA_BUF_PLANE0_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE0_PITCH_EXT, ++ EGL_DMA_BUF_PLANE1_FD_EXT, ++ EGL_DMA_BUF_PLANE1_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE1_PITCH_EXT, ++ EGL_DMA_BUF_PLANE2_FD_EXT, ++ EGL_DMA_BUF_PLANE2_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE2_PITCH_EXT, ++ EGL_DMA_BUF_PLANE3_FD_EXT, ++ EGL_DMA_BUF_PLANE3_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE3_PITCH_EXT ++ }; ++ ++ const EGLint * n = attnames; ++ ++ for (int i = 0; i < pic->i_planes; ++i) ++ { ++ *a++ = *n++; ++ *a++ = fd; ++ *a++ = *n++; ++ *a++ = pic->p[i].p_pixels - base_addr; ++ *a++ = *n++; ++ *a++ = pic->p[i].i_pitch; ++ } ++ } ++ ++ *a = EGL_NONE; ++ ++ const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); ++ if (!image) { ++ msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError()); ++ goto fail; ++ } ++ ++ // ** ?? tc->tex_target ++ tc->vt->GenTextures(1, &tex->texture); ++ tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); ++ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); ++ ++ tc->gl->egl.destroyImageKHR(tc->gl, image); ++ } ++ ++ if (cma_buf_pic_add_context2(pic, &tex->cmn) != VLC_SUCCESS) ++ { ++ msg_Err(tc, "%s: add_context2 failed", __func__); ++ goto fail; ++ } ++ return tex; ++ ++fail: ++ tex_context_delete(tex); ++ return NULL; ++} ++ ++ ++static int ++tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures, ++ const GLsizei *tex_width, const GLsizei *tex_height, ++ picture_t *pic, const size_t *plane_offset) ++{ ++ mmal_gl_converter_t * const sys = tc->priv; ++#if TRACE_ALL ++ msg_Err(tc, "%s: %d*%dx%d : %d*%dx%d", __func__, tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines); ++#endif ++ VLC_UNUSED(tex_width); ++ VLC_UNUSED(tex_height); ++ VLC_UNUSED(plane_offset); ++ ++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) ++ { ++ char cbuf[5]; ++ msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma)); ++ return VLC_EGENERIC; ++ } ++ ++ tex_context_t * const tex = get_tex_context(tc, pic); ++ if (tex == NULL) ++ return VLC_EGENERIC; ++ ++// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); ++ ++ cma_buf_pic_context_unref(sys->last_ctx_ref); // ?? Needed ?? ++ sys->last_ctx_ref = cma_buf_pic_context_ref(pic); ++ ++ textures[0] = tex->texture; ++ return VLC_SUCCESS; ++} ++ ++static int ++tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program) ++{ ++ tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0"); ++ return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC; ++} ++ ++static void ++tc_mmal_prepare_shader(const opengl_tex_converter_t *tc, ++ const GLsizei *tex_width, const GLsizei *tex_height, ++ float alpha) ++{ ++ (void) tex_width; (void) tex_height; (void) alpha; ++ VLC_UNUSED(tc); ++// tc->vt->Uniform1i(tc->uloc.Texture[0], 0); ++} ++ ++static GLuint ++tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target, ++ const vlc_fourcc_t chroma, const video_color_space_t yuv_space) ++{ ++ VLC_UNUSED(yuv_space); ++ ++ tc->tex_count = 1; ++ tc->tex_target = tex_target; ++ tc->texs[0] = (struct opengl_tex_cfg) { ++ { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT //** ?? ++ }; ++ ++ tc->pf_fetch_locations = tc_mmal_fetch_locations; ++ tc->pf_prepare_shader = tc_mmal_prepare_shader; ++ ++ ++ const char fs[] = ++ "#extension GL_OES_EGL_image_external : enable\n" ++ "precision mediump float;\n" ++ "uniform samplerExternalOES Texture0;\n" ++ "varying vec2 TexCoord0;\n" ++ "void main() {\n" ++ " gl_FragColor = texture2D(Texture0, TexCoord0);\n" ++ "}\n"; ++ ++ ++ const char *code = fs; ++ ++ GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER); ++ tc->vt->ShaderSource(fragment_shader, 1, &code, NULL); ++ tc->vt->CompileShader(fragment_shader); ++ return fragment_shader; ++} ++ ++ ++static void ++CloseGLConverter(vlc_object_t *obj) ++{ ++ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; ++ mmal_gl_converter_t * const sys = tc->priv; ++ ++ if (sys == NULL) ++ return; ++ ++ cma_buf_pic_context_unref(sys->last_ctx_ref); ++ cma_vcsm_exit(sys->vcsm_init_type); ++ free(sys); ++} ++ ++static int ++OpenGLConverter(vlc_object_t *obj) ++{ ++ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; ++ int rv = VLC_EGENERIC; ++ const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt); ++ ++ // Accept Opaque (as it can definitely be converted) or what we actually want ++ if (!(tc->fmt.i_chroma == FMT_IN || ++ tc->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE)) ++ { ++ return rv; ++ } ++ ++ if (tc->gl->ext != VLC_GL_EXT_EGL || ++ !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR) ++ { ++ // Missing an important callback ++ msg_Dbg(tc, "Missing EGL xxxImageKHR calls"); ++ return rv; ++ } ++ ++ { ++ char dbuf0[5], dbuf1[5]; ++ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, ++ str_fourcc(dbuf0, tc->fmt.i_chroma), ++ str_fourcc(dbuf1, eglfmt), ++ tc->fmt.i_width, tc->fmt.i_height, ++ tc->fmt.i_x_offset, tc->fmt.i_y_offset, ++ tc->fmt.i_visible_width, tc->fmt.i_visible_height, ++ tc->fmt.i_sar_num, tc->fmt.i_sar_den); ++ } ++ ++ if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL) ++ { ++ msg_Err(tc, "priv alloc failure"); ++ rv = VLC_ENOMEM; ++ goto fail; ++ } ++ mmal_gl_converter_t * const sys = tc->priv; ++ ++ sys->drm_fourcc = eglfmt; ++ ++ if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) { ++ msg_Dbg(tc, "VCSM init failed"); ++ goto fail; ++ } ++ ++ if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL) ++ { ++ msg_Err(tc, "Failed to bind GL fns"); ++ goto fail; ++ } ++ ++ if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES, ++ eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma, ++ eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0) ++ { ++ msg_Err(tc, "Failed to make shader"); ++ goto fail; ++ } ++ ++ if (eglfmt == 0) ++ { ++ tc->fmt.i_chroma = FMT_IN; ++ tc->fmt.i_bits_per_pixel = 8; ++ if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) ++ { ++ tc->fmt.i_rmask = 0xff0000; ++ tc->fmt.i_gmask = 0xff00; ++ tc->fmt.i_bmask = 0xff; ++ tc->fmt.space = COLOR_SPACE_SRGB; ++ } ++ else ++ { ++ tc->fmt.i_rmask = 0; ++ tc->fmt.i_gmask = 0; ++ tc->fmt.i_bmask = 0; ++ tc->fmt.space = COLOR_SPACE_UNDEF; ++ } ++ sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt); ++ } ++ ++ tc->handle_texs_gen = true; // We manage the texs ++ tc->pf_update = tc_mmal_update; ++ return VLC_SUCCESS; ++ ++fail: ++ CloseGLConverter(obj); ++ return rv; ++} ++ ++vlc_module_begin () ++ set_description("MMAL OpenGL surface converter") ++ set_shortname (N_("MMALGLConverter")) ++ set_capability("glconv", 900) ++ set_callbacks(OpenGLConverter, CloseGLConverter) ++ set_category(CAT_VIDEO) ++ set_subcategory(SUBCAT_VIDEO_VOUT) ++ add_shortcut("mmal_gl_converter") ++vlc_module_end () ++ +--- a/modules/hw/mmal/deinterlace.c ++++ b/modules/hw/mmal/deinterlace.c +@@ -26,11 +26,12 @@ + #include "config.h" + #endif + +-#include ++#include ++ + #include ++#include + #include + #include +-#include + + #include "mmal_picture.h" + +@@ -41,466 +42,569 @@ + + #define MIN_NUM_BUFFERS_IN_TRANSIT 2 + +-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu" +-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.") +-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.") ++#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu" ++#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.") ++#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.") + +-static int Open(filter_t *filter); +-static void Close(filter_t *filter); ++#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv" ++#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace") ++#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace") + +-vlc_module_begin() +- set_shortname(N_("MMAL deinterlace")) +- set_description(N_("MMAL-based deinterlace filter plugin")) +- set_capability("video filter", 0) +- set_category(CAT_VIDEO) +- set_subcategory(SUBCAT_VIDEO_VFILTER) +- set_callbacks(Open, Close) +- add_shortcut("deinterlace") +- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT, +- MMAL_DEINTERLACE_QPU_LONGTEXT, true); +-vlc_module_end() ++#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast" ++#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace") ++#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace") ++ ++#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none" ++#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace") ++#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\ ++ "This is the default for > SD if < 96M gpu-mem") ++ ++#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate" ++#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate") ++#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input") ++ ++#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate" ++#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate") ++#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input") + +-struct filter_sys_t { ++ ++typedef struct filter_sys_t ++{ + MMAL_COMPONENT_T *component; + MMAL_PORT_T *input; + MMAL_PORT_T *output; ++ MMAL_POOL_T *in_pool; ++ hw_mmal_port_pool_ref_t *out_ppr; + +- MMAL_QUEUE_T *filtered_pictures; +- vlc_sem_t sem; ++ MMAL_QUEUE_T * out_q; + +- atomic_bool started; ++ bool half_rate; ++ bool use_qpu; ++ bool use_fast; ++ bool use_passthrough; + unsigned int seq_in; + unsigned int seq_out; +} filter_sys_t; @@ -6146,48 +6703,548 @@ + set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) +vlc_module_end() + ---- a/modules/hw/mmal/mmal_picture.c -+++ b/modules/hw/mmal/mmal_picture.c -@@ -21,25 +21,961 @@ - * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -+// We would really like to use vlc_thread.h but the detach thread stuff can't be -+// used here :-( -+#include +--- /dev/null ++++ b/modules/hw/mmal/mmal_cma.c +@@ -0,0 +1,377 @@ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif + +#include ++#include ++#include ++#include ++#include + - #include - #include - #include -+#include -+#include -+#include +#include - - #include "mmal_picture.h" - --int mmal_picture_lock(picture_t *picture) + -+static void flush_range(void * start, size_t len) ++#include ++#include ++ ++#include "mmal_cma.h" ++ ++#include ++ ++ ++typedef void * cma_pool_alloc_fn(void * v, size_t size); ++typedef void cma_pool_free_fn(void * v, void * el, size_t size); ++ ++// Pool structure ++// Ref count is held by pool owner and pool els that have been got ++// Els in the pool do not count towards its ref count ++struct cma_pool_fixed_s ++{ ++ atomic_int ref_count; ++ ++ vlc_mutex_t lock; ++ unsigned int n_in; ++ unsigned int n_out; ++ unsigned int pool_size; ++ size_t el_size; ++ void ** pool; ++ ++ void * alloc_v; ++ cma_pool_alloc_fn * el_alloc_fn; ++ cma_pool_free_fn * el_free_fn; ++}; ++ ++typedef struct cma_buf_s { ++ size_t size; ++ unsigned int vcsm_h; // VCSM handle from initial alloc ++ unsigned int vc_h; // VC handle for ZC mmal buffers ++ int fd; // dmabuf handle for GL ++ void * mmap; // ARM mapped address ++ picture_context_t *ctx2; ++} cma_buf_t; ++ ++static int free_pool(const cma_pool_fixed_t * const p, void ** pool, unsigned int n, size_t el_size) ++{ ++ int i = 0; ++ assert(pool != NULL); ++ ++ while (pool[n] != NULL) ++ { ++ p->el_free_fn(p->alloc_v, pool[n], el_size); ++ pool[n] = NULL; ++ n = n + 1 < p->pool_size ? n + 1 : 0; ++ ++i; ++ } ++ free(pool); ++ return i; ++} ++ ++// Just kill this - no checks ++static void cma_pool_fixed_delete(cma_pool_fixed_t * const p) ++{ ++ if (p->pool != NULL) ++ free_pool(p, p->pool, p->n_in, p->el_size); ++ ++ vlc_mutex_destroy(&p->lock); ++ free(p); ++} ++ ++void cma_pool_fixed_unref(cma_pool_fixed_t * const p) ++{ ++ if (atomic_fetch_sub(&p->ref_count, 1) <= 1) ++ cma_pool_fixed_delete(p); ++} ++ ++void cma_pool_fixed_ref(cma_pool_fixed_t * const p) ++{ ++ atomic_fetch_add(&p->ref_count, 1); ++} ++ ++void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size) ++{ ++ void * v = NULL; ++ void ** deadpool = NULL; ++ size_t dead_size = 0; ++ unsigned int dead_n = 0; ++ ++ vlc_mutex_lock(&p->lock); ++ ++ if (req_el_size != p->el_size) ++ { ++ deadpool = p->pool; ++ dead_n = p->n_in; ++ dead_size = p->el_size; ++ ++ p->pool = NULL; ++ p->n_in = 0; ++ p->n_out = 0; ++ p->el_size = req_el_size; ++ } ++ else if (p->pool != NULL) ++ { ++ v = p->pool[p->n_in]; ++ if (v != NULL) ++ { ++ p->pool[p->n_in] = NULL; ++ p->n_in = p->n_in + 1 < p->pool_size ? p->n_in + 1 : 0; ++ } ++ } ++ ++ vlc_mutex_unlock(&p->lock); ++ ++ // Do the free old op outside the mutex in case the free is slow ++ if (deadpool != NULL) ++ free_pool(p, deadpool, dead_n, dead_size); ++ ++ if (v == NULL && req_el_size != 0) ++ v = p->el_alloc_fn(p->alloc_v, req_el_size); ++ ++ // Tag ref ++ if (v != NULL) ++ cma_pool_fixed_ref(p); ++ ++ return v; ++} ++ ++void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size) ++{ ++ vlc_mutex_lock(&p->lock); ++ ++ if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL)) ++ { ++ if (p->pool == NULL) ++ p->pool = calloc(p->pool_size, sizeof(void*)); ++ ++ p->pool[p->n_out] = v; ++ p->n_out = p->n_out + 1 < p->pool_size ? p->n_out + 1 : 0; ++ v = NULL; ++ } ++ ++ vlc_mutex_unlock(&p->lock); ++ ++ if (v != NULL) ++ p->el_free_fn(p->alloc_v, v, el_size); ++ ++ cma_pool_fixed_unref(p); ++} ++ ++// Purge pool & unref ++void cma_pool_fixed_kill(cma_pool_fixed_t * const p) ++{ ++ // This flush is not strictly needed but it reclaims what memory we can reclaim asap ++ cma_pool_fixed_get(p, 0); ++ cma_pool_fixed_unref(p); ++} ++ ++cma_pool_fixed_t* ++cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v, ++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn) ++{ ++ cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t)); ++ if (p == NULL) ++ return NULL; ++ ++ atomic_store(&p->ref_count, 1); ++ vlc_mutex_init(&p->lock); ++ ++ p->pool_size = pool_size; ++ ++ p->alloc_v = alloc_v; ++ p->el_alloc_fn = alloc_fn; ++ p->el_free_fn = free_fn; ++ ++ return p; ++} ++ ++ ++static void cma_pool_delete(cma_buf_t * const cb) ++{ ++ if (cb->ctx2 != NULL) ++ cb->ctx2->destroy(cb->ctx2); ++ ++ if (cb->mmap != MAP_FAILED) ++ munmap(cb->mmap, cb->size); ++ if (cb->fd != -1) ++ close(cb->fd); ++ if (cb->vcsm_h != 0) ++ vcsm_free(cb->vcsm_h); ++ free(cb); ++} ++ ++static void cma_pool_free_cb(void * v, void * el, size_t size) ++{ ++ VLC_UNUSED(v); ++ VLC_UNUSED(size); ++ ++ cma_pool_delete(el); ++} ++ ++static void * cma_pool_alloc_cb(void * v, size_t size) ++{ ++ VLC_UNUSED(v); ++ ++ cma_buf_t * const cb = malloc(sizeof(cma_buf_t)); ++ if (cb == NULL) ++ return NULL; ++ ++ *cb = (cma_buf_t){ ++ .size = size, ++ .vcsm_h = 0, ++ .vc_h = 0, ++ .fd = -1, ++ .mmap = MAP_FAILED, ++ .ctx2 = NULL ++ }; ++ ++ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST, (char*)"VLC frame")) == 0) ++ goto fail; ++ ++ if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0) ++ goto fail; ++ ++ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1) ++ goto fail; ++ ++ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED) ++ goto fail; ++ ++ return cb; ++ ++fail: ++ cma_pool_delete(cb); ++ return NULL; ++} ++ ++void cma_buf_pool_delete(cma_pool_fixed_t * const p) ++{ ++ assert(p != NULL); ++ ++ cma_pool_fixed_kill(p); ++} ++ ++cma_pool_fixed_t * cma_buf_pool_new(void) ++{ ++ return cma_pool_fixed_new(5, NULL, cma_pool_alloc_cb, cma_pool_free_cb); ++} ++ ++ ++typedef struct cma_pic_context_s { ++ picture_context_t cmn; ++ ++ atomic_int ref_count; ++ cma_pool_fixed_t * p; ++ cma_buf_t * cb; ++} cma_pic_context_t; ++ ++ ++static void cma_buf_pic_ctx_ref(cma_pic_context_t * const ctx) +{ -+ struct vcsm_user_clean_invalid2_s * b = calloc(1, -+ sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s)); ++ atomic_fetch_add(&ctx->ref_count, 1); ++} + -+ b->op_count = 1, ++static void cma_buf_pic_ctx_unref(cma_pic_context_t * const ctx) ++{ ++ if (atomic_fetch_sub(&ctx->ref_count, 1) > 0) ++ return; ++ ++ if (ctx->cb != NULL) ++ cma_pool_fixed_put(ctx->p, ctx->cb, ctx->cb->size); ++ ++ free(ctx); ++} ++ ++static picture_context_t * cma_buf_pic_ctx_copy(picture_context_t * pic_ctx) ++{ ++ cma_buf_pic_ctx_ref((cma_pic_context_t *)pic_ctx); ++ return pic_ctx; ++} ++ ++static void cma_buf_pic_ctx_destroy(picture_context_t * pic_ctx) ++{ ++ cma_buf_pic_ctx_unref((cma_pic_context_t *)pic_ctx); ++} ++ ++int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size) ++{ ++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) ++ return VLC_EGENERIC; ++ if (pic->context != NULL) ++ return VLC_EBADVAR; ++ ++ cma_buf_t * const cb = cma_pool_fixed_get(p, size); ++ if (cb == NULL) ++ return VLC_ENOMEM; ++ ++ cma_pic_context_t * const ctx = malloc(sizeof(cma_pic_context_t)); ++ if (ctx == NULL) ++ goto fail; ++ ++ *ctx = (cma_pic_context_t){ ++ .cmn = { ++ .destroy = cma_buf_pic_ctx_destroy, ++ .copy = cma_buf_pic_ctx_copy ++ }, ++ .ref_count = 0, ++ .p = p, ++ .cb = cb ++ }; ++ ++ pic->context = &ctx->cmn; ++ return VLC_SUCCESS; ++ ++fail: ++ cma_pool_fixed_put(p, cb, size); ++ return VLC_EGENERIC; ++} ++ ++int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL || ctx->cb->ctx2 != NULL) ++ return VLC_EGENERIC; ++ ++ ctx->cb->ctx2 = ctx2; ++ return VLC_SUCCESS; ++} ++ ++unsigned int cma_buf_pic_vc_handle(const picture_t * const pic) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb == NULL ? 0 : ctx->cb->vc_h; ++} ++ ++int cma_buf_pic_fd(const picture_t * const pic) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? -1 : ctx->cb == NULL ? -1 : ctx->cb->fd; ++} ++ ++void * cma_buf_pic_addr(const picture_t * const pic) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->mmap; ++} ++ ++picture_context_t * cma_buf_pic_context2(const picture_t * const pic) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->ctx2; ++} ++ ++cma_pic_context_t * cma_buf_pic_context_ref(const picture_t * const pic) ++{ ++ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context; ++ ++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL) ++ return NULL; ++ ++ cma_buf_pic_ctx_ref(ctx); ++ return ctx; ++} ++ ++void cma_buf_pic_context_unref(cma_pic_context_t * const ctx) ++{ ++ if (ctx != NULL) ++ cma_buf_pic_ctx_unref(ctx); ++} ++ ++ +--- /dev/null ++++ b/modules/hw/mmal/mmal_cma.h +@@ -0,0 +1,45 @@ ++ ++struct cma_pool_fixed_s; ++typedef struct cma_pool_fixed_s cma_pool_fixed_t; ++ ++typedef void * cma_pool_alloc_fn(void * v, size_t size); ++typedef void cma_pool_free_fn(void * v, void * el, size_t size); ++ ++void cma_pool_fixed_unref(cma_pool_fixed_t * const p); ++void cma_pool_fixed_ref(cma_pool_fixed_t * const p); ++void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size); ++void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size); ++void cma_pool_fixed_kill(cma_pool_fixed_t * const p); ++cma_pool_fixed_t* cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v, ++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn); ++ ++void cma_buf_pool_delete(cma_pool_fixed_t * const p); ++cma_pool_fixed_t * cma_buf_pool_new(void); ++ ++int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size); ++int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2); ++unsigned int cma_buf_pic_vc_handle(const picture_t * const pic); ++int cma_buf_pic_fd(const picture_t * const pic); ++void * cma_buf_pic_addr(const picture_t * const pic); ++picture_context_t * cma_buf_pic_context2(const picture_t * const pic); ++struct cma_pic_context_s; ++struct cma_pic_context_s * cma_buf_pic_context_ref(const picture_t * const pic); ++void cma_buf_pic_context_unref(struct cma_pic_context_s * const ctx); ++ ++#include ++ ++static inline bool is_cma_buf_pic_chroma(const uint32_t chroma) ++{ ++ return chroma == VLC_CODEC_MMAL_ZC_RGB32 || chroma == VLC_CODEC_MMAL_ZC_SAND8 || chroma == VLC_CODEC_MMAL_ZC_I420; ++} ++ ++static inline void cma_buf_pool_deletez(cma_pool_fixed_t ** const pp) ++{ ++ cma_pool_fixed_t * const p = *pp; ++ if (p != NULL) { ++ *pp = NULL; ++ cma_buf_pool_delete(p); ++ } ++} ++ ++ +--- /dev/null ++++ b/modules/hw/mmal/mmal_gl.h +@@ -0,0 +1,45 @@ ++// Trim this include list! ++ ++#include ++#include ++#include ++//#include ++//#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct mmal_gl_converter_s; ++ ++typedef struct cma_buf_s { ++ struct mmal_gl_converter_s * sys; ++ ++ size_t size; ++ __u32 h_dumb; ++ int fd; ++ unsigned int h_vcsm; ++ void * mapped_addr; ++ GLuint texture; ++} cma_buf_t; ++ ++typedef struct cma_pic_sys_s { ++ cma_buf_t * cmabuf; ++} cma_pic_sys_t; ++ ++static inline unsigned int ++hw_mmal_h_vcsm(const picture_t * const pic) ++{ ++ const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys; ++ ++ if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 || ++ pic_sys == NULL || pic_sys->cmabuf == NULL) { ++ return 0; ++ } ++ ++ return pic_sys->cmabuf->h_vcsm; ++} ++ +--- a/modules/hw/mmal/mmal_picture.c ++++ b/modules/hw/mmal/mmal_picture.c +@@ -21,25 +21,1034 @@ + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + ++// We would really like to use vlc_thread.h but the detach thread stuff can't be ++// used here :-( ++#include ++ ++#include ++#include ++#include ++ + #include + #include + #include ++#include ++#include ++#include ++#include ++ ++#include "mmal_cma.h" // ************** + + #include "mmal_picture.h" + +-int mmal_picture_lock(picture_t *picture) ++#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t)) ++ ++static inline char safe_char(const unsigned int c0) ++{ ++ const unsigned int c = c0 & 0xff; ++ return c > ' ' && c < 0x7f ? c : '.'; ++} ++ ++const char * str_fourcc(char * const buf, const unsigned int fcc) ++{ ++ if (fcc == 0) ++ return "----"; ++ buf[0] = safe_char(fcc >> 0); ++ buf[1] = safe_char(fcc >> 8); ++ buf[2] = safe_char(fcc >> 16); ++ buf[3] = safe_char(fcc >> 24); ++ buf[4] = 0; ++ return buf; ++} ++ ++// WB + Inv ++static inline void flush_range(void * const start, const size_t len) ++{ ++ uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))]; ++ struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf; ++ ++ *b = (struct vcsm_user_clean_invalid2_s){ ++ .op_count = 1 ++ }; + + b->s[0] = (struct vcsm_user_clean_invalid2_block_s){ -+ .invalidate_mode = 3, ++ .invalidate_mode = 3, // wb + invalidate + .block_count = 1, -+ .start_address = start, ++ .start_address = start, // Rely on clean inv to fix up align & size boundries + .block_size = len, + .inter_block_stride = 0 + }; + + vcsm_clean_invalid2(b); -+ -+ free(b); +} + +MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs) @@ -6207,6 +7264,7 @@ +MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc) +{ + switch (vf_vlc->i_chroma) { ++ case VLC_CODEC_MMAL_ZC_RGB32: + case VLC_CODEC_RGB32: + { + // VLC RGB32 aka RV32 means we have to look at the mask values @@ -6223,6 +7281,8 @@ + return MMAL_ENCODING_ARGB; + break; + } ++ case VLC_CODEC_MMAL_ZC_I420: ++ return MMAL_ENCODING_I420; + case VLC_CODEC_RGBA: + return MMAL_ENCODING_RGBA; + case VLC_CODEC_BGRA: @@ -6347,7 +7407,9 @@ + hw_mmal_port_pool_ref_t ** pppr, + MMAL_PORT_T * const port, + const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback) -+{ + { +- picture_sys_t *pic_sys = picture->p_sys; +- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; + MMAL_STATUS_T status; + + port->userdata = (struct MMAL_PORT_USERDATA_T *)obj; @@ -6382,14 +7444,20 @@ + msg_Err(obj, "Failed to create output pool"); + return status; + } -+ + +- int offset = 0; +- picture->p[0].p_pixels = buffer->data; +- for (int i = 1; i < picture->i_planes; i++) { +- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines; +- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset; + status = mmal_port_enable(port, callback); + if (status != MMAL_SUCCESS) { + msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)", + port->name, status, mmal_status_to_string(status)); + return status; -+ } -+ + } + +- pic_sys->displayed = false; + return MMAL_SUCCESS; +} + @@ -6443,7 +7511,8 @@ + + return MMAL_TRUE; +} -+ + +- return VLC_SUCCESS; +// Buffer belongs to context on successful return from this fn +// is still valid on failure +picture_context_t * @@ -6683,7 +7752,7 @@ + ent = ent->prev; + } + return NULL; -+} + } + +#define POOL_ENT_ALLOC_BLOCK 0x10000 + @@ -6800,6 +7869,8 @@ +} + + ++const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 }; ++ +void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH) +{ + const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; @@ -6841,15 +7912,6 @@ + return &sb->dreg; +} + -+void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h) -+{ -+ vzc_subbuf_ent_t * sb = buf->user_data; -+ sb->orig_dest_rect.x = x; -+ sb->orig_dest_rect.y = y; -+ sb->orig_dest_rect.width = w; -+ sb->orig_dest_rect.height = h; -+} -+ +static inline int rescale_x(int x, int mul, int div) +{ + return div == 0 ? x * mul : (x * mul + div/2) / div; @@ -6864,9 +7926,7 @@ +} + +void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect) - { -- picture_sys_t *pic_sys = picture->p_sys; -- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; ++{ + vzc_subbuf_ent_t * sb = buf->user_data; + if (scale_rect == NULL) { + sb->dreg.dest_rect = sb->orig_dest_rect; @@ -6891,7 +7951,14 @@ +// no rules governing the order in which things are blended) so we must deal +// (fairly) gracefully with it never (or always) being set. + -+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first) ++// dst_fmt gives the number space in which the destination pixels are specified ++ ++MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, ++ picture_t * const pic, ++ const MMAL_RECT_T dst_pic_rect, ++ const int x_offset, const int y_offset, ++ const unsigned int alpha, ++ const bool is_first) +{ + MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue); + vzc_subbuf_ent_t * sb; @@ -6967,22 +8034,33 @@ + }; + + // Remember offsets -+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT; ++ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT | ++ MMAL_DISPLAY_SET_DEST_RECT | ++ MMAL_DISPLAY_SET_FULLSCREEN | ++ MMAL_DISPLAY_SET_ALPHA; ++ ++ sb->dreg.fullscreen = 0; ++ // Will be set later - zero now to avoid any confusion ++ sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0}; ++ ++ sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX; + +// printf("+++ bpp:%d, vis:%dx%d wxh:%dx%d, d:%dx%d\n", bpp, fmt->i_visible_width, fmt->i_visible_height, fmt->i_width, fmt->i_height, dst_stride, dst_lines); + + sb->dreg.src_rect = (MMAL_RECT_T){ -+ .x = (fmt->i_x_offset - xl), -+ .y = 0, -+ .width = fmt->i_visible_width, ++ .x = (fmt->i_x_offset - xl), ++ .y = 0, ++ .width = fmt->i_visible_width, + .height = fmt->i_visible_height + }; + -+ sb->pic_rect = (MMAL_RECT_T){ -+ .x = dst_pic->format.i_x_offset, -+ .y = dst_pic->format.i_y_offset, -+ .width = dst_pic->format.i_visible_width, -+ .height = dst_pic->format.i_visible_height ++ sb->pic_rect = dst_pic_rect; ++ ++ sb->orig_dest_rect = (MMAL_RECT_T){ ++ .x = x_offset, ++ .y = y_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height + }; + + if (needs_copy) @@ -7021,12 +8099,7 @@ + pool_recycle_list(pc, &pc->ents_prev); + pool_recycle_list(pc, &pc->ents_cur); +} - -- int offset = 0; -- picture->p[0].p_pixels = buffer->data; -- for (int i = 1; i < picture->i_planes; i++) { -- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines; -- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset; ++ +static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc) +{ + @@ -7107,22 +8180,55 @@ + { + hw_mmal_vzc_pool_delete(pc); + return NULL; - } - -- pic_sys->displayed = false; ++ } ++ + atomic_store(&pc->ref_count, 1); + + mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc); - -- return VLC_SUCCESS; ++ + return pc; - } ++} ++ + ++//---------------------------------------------------------------------------- ++ ++vcsm_init_type_t cma_vcsm_init(void) ++{ ++ if (vcsm_init_ex(1, -1) == 0) { ++ return VCSM_INIT_CMA; ++ } ++ else if (vcsm_init_ex(0, -1) == 0) { ++ return VCSM_INIT_LEGACY; ++ } ++ return VCSM_INIT_NONE; ++} ++ ++void cma_vcsm_exit(const vcsm_init_type_t init_mode) ++{ ++ if (init_mode != VCSM_INIT_NONE) ++ vcsm_exit(); ++} ++ ++const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode) ++{ ++ switch (init_mode) ++ { ++ case VCSM_INIT_CMA: ++ return "CMA"; ++ case VCSM_INIT_LEGACY: ++ return "Legacy"; ++ case VCSM_INIT_NONE: ++ return "none"; ++ default: ++ break; ++ } ++ return "???"; ++} + + --- a/modules/hw/mmal/mmal_picture.h +++ b/modules/hw/mmal/mmal_picture.h -@@ -24,19 +24,243 @@ +@@ -24,19 +24,275 @@ #ifndef VLC_MMAL_MMAL_PICTURE_H_ #define VLC_MMAL_MMAL_PICTURE_H_ @@ -7158,10 +8264,7 @@ + + +#define CTX_BUFS_MAX 4 - -- MMAL_BUFFER_HEADER_T *buffer; -- bool displayed; --}; ++ +typedef struct pic_ctx_mmal_s { + picture_context_t cmn; // PARENT: Common els at start + @@ -7180,8 +8283,9 @@ + vlc_object_t * obj; +#endif +} pic_ctx_mmal_t; - --int mmal_picture_lock(picture_t *picture); ++ ++const char * str_fourcc(char * const buf, const unsigned int fcc); ++ +MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc); +MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs); +void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc); @@ -7230,7 +8334,8 @@ + return pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE || + pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 || + pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || -+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420; ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 || ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32; +} + +static inline MMAL_FOURCC_T hw_mmal_pic_format(const picture_t *const pic) @@ -7341,21 +8446,28 @@ +struct vzc_pool_ctl_s; +typedef struct vzc_pool_ctl_s vzc_pool_ctl_t; + ++// At the moment we cope with any mono-planar RGBA thing ++// We could cope with many other things but they currently don't occur ++extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[]; +static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc) +{ + const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma; -+ // At the moment we cope with any mono-planar RGBA thing -+ // We could cope with many other things but they currently don't occur -+ return vfcc_src == VLC_CODEC_RGBA || vfcc_src == VLC_CODEC_BGRA || vfcc_src == VLC_CODEC_ARGB; ++ for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p) ++ if (*p == vfcc_src) ++ return true; ++ ++ return false; +} + +bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt); +MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf); -+void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h); +void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect); +void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH); +unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf); -+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first); ++MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, ++ const MMAL_RECT_T dst_pic_rect, ++ const int x_offset, const int y_offset, ++ const unsigned int alpha, const bool is_first); +void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf, + uint32_t * const pWidth, uint32_t * const pHeight); + @@ -7364,9 +8476,35 @@ +void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc); +vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void); + ++ ++static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt) ++{ ++ return (MMAL_RECT_T){ ++ .x = fmt->i_x_offset, ++ .y = fmt->i_y_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height ++ }; ++} ++ ++typedef enum vcsm_init_type_e { ++ VCSM_INIT_NONE = 0, ++ VCSM_INIT_LEGACY, ++ VCSM_INIT_CMA ++} vcsm_init_type_t; ++ ++vcsm_init_type_t cma_vcsm_init(void); ++void cma_vcsm_exit(const vcsm_init_type_t init_mode); ++const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode); ++ + +- MMAL_BUFFER_HEADER_T *buffer; +- bool displayed; +-}; +#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024 +#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0) -+ + +-int mmal_picture_lock(picture_t *picture); +#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize" +#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp" +#define MMAL_COMPONENT_HVS "vc.ril.hvs" @@ -7487,7 +8625,7 @@ + --- /dev/null +++ b/modules/hw/mmal/subpic.c -@@ -0,0 +1,222 @@ +@@ -0,0 +1,227 @@ +/***************************************************************************** + * mmal.c: MMAL-based decoder plugin for Raspberry Pi + ***************************************************************************** @@ -7595,13 +8733,13 @@ + + +int hw_mmal_subpic_update(vlc_object_t * const p_filter, -+ picture_t * const p_pic, const unsigned int sub_no, ++ MMAL_BUFFER_HEADER_T * const sub_buf, + subpic_reg_stash_t * const spe, ++ const video_format_t * const fmt, + const MMAL_RECT_T * const scale_out, + const uint64_t pts) +{ + MMAL_STATUS_T err; -+ MMAL_BUFFER_HEADER_T * const sub_buf = hw_mmal_pic_sub_buf_get(p_pic, sub_no); + + if (sub_buf == NULL) + { @@ -7647,24 +8785,29 @@ + MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf); + MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video; + -+ v_fmt->frame_rate.den = p_pic->format.i_frame_rate_base; -+ v_fmt->frame_rate.num = p_pic->format.i_frame_rate; -+ v_fmt->par.den = p_pic->format.i_sar_den; -+ v_fmt->par.num = p_pic->format.i_sar_num; ++ v_fmt->frame_rate.den = fmt->i_frame_rate_base; ++ v_fmt->frame_rate.num = fmt->i_frame_rate; ++ v_fmt->par.den = fmt->i_sar_den; ++ v_fmt->par.num = fmt->i_sar_num; + v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN; + -+ + if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) { + + spe->alpha = dreg->alpha; + spe->dest_rect = dreg->dest_rect; + needs_update = true; -+#if TRACE_ALL -+ msg_Dbg(p_filter, "Update region for sub %d", sub_no); -+#endif ++ + dreg->layer = spe->layer; + dreg->set |= MMAL_DISPLAY_SET_LAYER; + ++#if TRACE_ALL ++ msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x", ++ __func__, dreg->set, ++ dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y, ++ dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y, ++ dreg->layer, dreg->alpha); ++#endif ++ + if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS) + { + msg_Err(p_filter, "Set display region on subput failed"); @@ -7712,7 +8855,7 @@ + --- /dev/null +++ b/modules/hw/mmal/subpic.h -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +#ifndef VLC_HW_MMAL_SUBPIC_H_ +#define VLC_HW_MMAL_SUBPIC_H_ + @@ -7728,10 +8871,11 @@ +} subpic_reg_stash_t; + +int hw_mmal_subpic_update(vlc_object_t * const p_filter, -+ picture_t * const p_pic, const unsigned int sub_no, -+ subpic_reg_stash_t * const stash, -+ const MMAL_RECT_T * const scale_out, -+ const uint64_t pts); ++ MMAL_BUFFER_HEADER_T * const sub_buf, ++ subpic_reg_stash_t * const spe, ++ const video_format_t * const fmt, ++ const MMAL_RECT_T * const scale_out, ++ const uint64_t pts); + +void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); + @@ -7744,385 +8888,385 @@ --- /dev/null +++ b/modules/hw/mmal/v7_pmu.S @@ -0,0 +1,263 @@ -+/*------------------------------------------------------------ -+Performance Monitor Block -+------------------------------------------------------------*/ -+ .arm @ Make sure we are in ARM mode. -+ .text -+ .align 2 -+ .global getPMN @ export this function for the linker -+ -+/* Returns the number of progammable counters uint32_t getPMN(void) */ -+ -+getPMN: -+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */ -+ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */ -+ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */ -+ BX lr -+ -+ -+ -+ .global pmn_config @ export this function for the linker -+ /* Sets the event for a programmable counter to record */ -+ /* void pmn_config(unsigned counter, uint32_t event) */ -+ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */ -+ /* event = r1 = The event code */ -+pmn_config: -+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ -+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ -+ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */ -+ BX lr -+ -+ -+ -+ .global ccnt_divider @ export this function for the linker -+ /* Enables/disables the divider (1/64) on CCNT */ -+ /* void ccnt_divider(int divider) */ -+ /* divider = r0 = If 0 disable divider, else enable dvider */ -+ccnt_divider: -+ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */ -+ -+ CMP r0, #0x0 /* IF (r0 == 0) */ -+ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */ -+ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */ -+ -+ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */ -+ BX lr -+ -+ -+ /* --------------------------------------------------------------- */ -+ /* Enable/Disable */ -+ /* --------------------------------------------------------------- */ -+ -+ .global enable_pmu @ export this function for the linker -+ /* Global PMU enable */ -+ /* void enable_pmu(void) */ -+enable_pmu: -+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ -+ ORR r0, r0, #0x01 /* Set E bit */ -+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ -+ BX lr -+ -+ -+ -+ .global disable_pmu @ export this function for the linker -+ /* Global PMU disable */ -+ /* void disable_pmu(void) */ -+disable_pmu: -+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ -+ BIC r0, r0, #0x01 /* Clear E bit */ -+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ -+ BX lr -+ -+ -+ -+ .global enable_ccnt @ export this function for the linker -+ /* Enable the CCNT */ -+ /* void enable_ccnt(void) */ -+enable_ccnt: -+ MOV r0, #0x80000000 /* Set C bit */ -+ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */ -+ BX lr -+ -+ -+ -+ .global disable_ccnt @ export this function for the linker -+ /* Disable the CCNT */ -+ /* void disable_ccnt(void) */ -+disable_ccnt: -+ MOV r0, #0x80000000 /* Clear C bit */ -+ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */ -+ BX lr -+ -+ -+ -+ .global enable_pmn @ export this function for the linker -+ /* Enable PMN{n} */ -+ /* void enable_pmn(uint32_t counter) */ -+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) -+enable_pmn: */ -+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ -+ MOV r1, r1, LSL r0 -+ -+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ -+ BX lr -+ -+ -+ -+ .global disable_pmn @ export this function for the linker -+ /* Enable PMN{n} */ -+ /* void disable_pmn(uint32_t counter) */ -+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) -+disable_pmn: */ -+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ -+ MOV r1, r1, LSL r0 -+ -+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ -+ BX lr -+ -+ -+ -+ .global enable_pmu_user_access @ export this function for the linker -+ /* Enables User mode access to the PMU (must be called in a priviledge */ -+ /* void enable_pmu_user_access(void) */ -+enable_pmu_user_access: -+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ -+ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */ -+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ -+ BX lr -+ -+ -+ -+ .global disable_pmu_user_access @ export this function for the linke -+ /* Disables User mode access to the PMU (must be called in a priviledg */ -+ /* void disable_pmu_user_access(void) */ -+disable_pmu_user_access: -+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ -+ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */ -+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ -+ BX lr -+ -+ -+ /* --------------------------------------------------------------- */ -+ /* Counter read registers */ -+ /* --------------------------------------------------------------- */ -+ -+ .global read_ccnt @ export this function for the linker -+ /* Returns the value of CCNT */ -+ /* uint32_t read_ccnt(void) */ -+read_ccnt: -+ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */ -+ BX lr -+ -+ -+ .global read_pmn @ export this function for the linker -+ /* Returns the value of PMN{n} */ -+ /* uint32_t read_pmn(uint32_t counter) */ -+ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) * -+read_pmn: */ -+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ -+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ -+ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */ -+ BX lr -+ -+ -+ /* --------------------------------------------------------------- */ -+ /* Software Increment */ -+ /* --------------------------------------------------------------- */ -+ -+ .global pmu_software_increment @ export this function for the linker -+ /* Writes to software increment register */ -+ /* void pmu_software_increment(uint32_t counter) */ -+ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN -+pmu_software_increment: */ -+ MOV r1, #0x01 -+ MOV r1, r1, LSL r0 -+ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */ -+ BX lr -+ -+ /* --------------------------------------------------------------- */ -+ /* Overflow & Interrupt Generation */ -+ /* --------------------------------------------------------------- */ -+ -+ .global read_flags @ export this function for the linker -+ /* Returns the value of the overflow flags */ -+ /* uint32_t read_flags(void) */ -+read_flags: -+ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */ -+ BX lr -+ -+ -+ .global write_flags @ export this function for the linker -+ /* Writes the overflow flags */ -+ /* void write_flags(uint32_t flags) */ -+write_flags: -+ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */ -+ BX lr -+ -+ -+ .global enable_ccnt_irq @ export this function for the linker -+ /* Enables interrupt generation on overflow of the CCNT */ -+ /* void enable_ccnt_irq(void) */ -+enable_ccnt_irq: -+ MOV r0, #0x80000000 -+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ -+ BX lr -+ -+ .global disable_ccnt_irq @ export this function for the linker -+ /* Disables interrupt generation on overflow of the CCNT */ -+ /* void disable_ccnt_irq(void) */ -+disable_ccnt_irq: -+ MOV r0, #0x80000000 -+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ -+ BX lr -+ -+ -+ .global enable_pmn_irq @ export this function for the linker -+ /* Enables interrupt generation on overflow of PMN{x} */ -+ /* void enable_pmn_irq(uint32_t counter) */ -+ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for -+enable_pmn_irq: */ -+ MOV r1, #0x1 /* Use arg (r0) to set which counter */ -+ MOV r0, r1, LSL r0 -+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ -+ BX lr -+ -+ .global disable_pmn_irq @ export this function for the linker -+ /* Disables interrupt generation on overflow of PMN{x} */ -+ /* void disable_pmn_irq(uint32_t counter) */ -+ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo -+disable_pmn_irq: */ -+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ -+ MOV r0, r1, LSL r0 -+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ -+ BX lr -+ -+ /* --------------------------------------------------------------- */ -+ /* Reset Functions */ -+ /* --------------------------------------------------------------- */ -+ -+ .global reset_pmn @ export this function for the linker -+ /* Resets the programmable counters */ -+ /* void reset_pmn(void) */ -+reset_pmn: -+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ -+ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */ -+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ -+ BX lr -+ -+ -+ .global reset_ccnt @ export this function for the linker -+ /* Resets the CCNT */ -+ /* void reset_ccnt(void) */ -+reset_ccnt: -+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ -+ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */ -+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ -+ BX lr -+ -+ -+ .end @end of code, this line is optional. -+/* ------------------------------------------------------------ */ -+/* End of v7_pmu.s */ -+/* ------------------------------------------------------------ */ -+ -+ ++/*------------------------------------------------------------ ++Performance Monitor Block ++------------------------------------------------------------*/ ++ .arm @ Make sure we are in ARM mode. ++ .text ++ .align 2 ++ .global getPMN @ export this function for the linker ++ ++/* Returns the number of progammable counters uint32_t getPMN(void) */ ++ ++getPMN: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */ ++ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */ ++ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */ ++ BX lr ++ ++ ++ ++ .global pmn_config @ export this function for the linker ++ /* Sets the event for a programmable counter to record */ ++ /* void pmn_config(unsigned counter, uint32_t event) */ ++ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */ ++ /* event = r1 = The event code */ ++pmn_config: ++ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ ++ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ ++ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */ ++ BX lr ++ ++ ++ ++ .global ccnt_divider @ export this function for the linker ++ /* Enables/disables the divider (1/64) on CCNT */ ++ /* void ccnt_divider(int divider) */ ++ /* divider = r0 = If 0 disable divider, else enable dvider */ ++ccnt_divider: ++ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */ ++ ++ CMP r0, #0x0 /* IF (r0 == 0) */ ++ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */ ++ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */ ++ ++ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Enable/Disable */ ++ /* --------------------------------------------------------------- */ ++ ++ .global enable_pmu @ export this function for the linker ++ /* Global PMU enable */ ++ /* void enable_pmu(void) */ ++enable_pmu: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x01 /* Set E bit */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ ++ .global disable_pmu @ export this function for the linker ++ /* Global PMU disable */ ++ /* void disable_pmu(void) */ ++disable_pmu: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ BIC r0, r0, #0x01 /* Clear E bit */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ ++ .global enable_ccnt @ export this function for the linker ++ /* Enable the CCNT */ ++ /* void enable_ccnt(void) */ ++enable_ccnt: ++ MOV r0, #0x80000000 /* Set C bit */ ++ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global disable_ccnt @ export this function for the linker ++ /* Disable the CCNT */ ++ /* void disable_ccnt(void) */ ++disable_ccnt: ++ MOV r0, #0x80000000 /* Clear C bit */ ++ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */ ++ BX lr ++ ++ ++ ++ .global enable_pmn @ export this function for the linker ++ /* Enable PMN{n} */ ++ /* void enable_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++enable_pmn: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r1, r1, LSL r0 ++ ++ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global disable_pmn @ export this function for the linker ++ /* Enable PMN{n} */ ++ /* void disable_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++disable_pmn: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r1, r1, LSL r0 ++ ++ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global enable_pmu_user_access @ export this function for the linker ++ /* Enables User mode access to the PMU (must be called in a priviledge */ ++ /* void enable_pmu_user_access(void) */ ++enable_pmu_user_access: ++ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ ++ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */ ++ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ ++ BX lr ++ ++ ++ ++ .global disable_pmu_user_access @ export this function for the linke ++ /* Disables User mode access to the PMU (must be called in a priviledg */ ++ /* void disable_pmu_user_access(void) */ ++disable_pmu_user_access: ++ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ ++ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */ ++ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Counter read registers */ ++ /* --------------------------------------------------------------- */ ++ ++ .global read_ccnt @ export this function for the linker ++ /* Returns the value of CCNT */ ++ /* uint32_t read_ccnt(void) */ ++read_ccnt: ++ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */ ++ BX lr ++ ++ ++ .global read_pmn @ export this function for the linker ++ /* Returns the value of PMN{n} */ ++ /* uint32_t read_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) * ++read_pmn: */ ++ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ ++ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ ++ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Software Increment */ ++ /* --------------------------------------------------------------- */ ++ ++ .global pmu_software_increment @ export this function for the linker ++ /* Writes to software increment register */ ++ /* void pmu_software_increment(uint32_t counter) */ ++ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN ++pmu_software_increment: */ ++ MOV r1, #0x01 ++ MOV r1, r1, LSL r0 ++ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */ ++ BX lr ++ ++ /* --------------------------------------------------------------- */ ++ /* Overflow & Interrupt Generation */ ++ /* --------------------------------------------------------------- */ ++ ++ .global read_flags @ export this function for the linker ++ /* Returns the value of the overflow flags */ ++ /* uint32_t read_flags(void) */ ++read_flags: ++ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */ ++ BX lr ++ ++ ++ .global write_flags @ export this function for the linker ++ /* Writes the overflow flags */ ++ /* void write_flags(uint32_t flags) */ ++write_flags: ++ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */ ++ BX lr ++ ++ ++ .global enable_ccnt_irq @ export this function for the linker ++ /* Enables interrupt generation on overflow of the CCNT */ ++ /* void enable_ccnt_irq(void) */ ++enable_ccnt_irq: ++ MOV r0, #0x80000000 ++ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ ++ BX lr ++ ++ .global disable_ccnt_irq @ export this function for the linker ++ /* Disables interrupt generation on overflow of the CCNT */ ++ /* void disable_ccnt_irq(void) */ ++disable_ccnt_irq: ++ MOV r0, #0x80000000 ++ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ ++ BX lr ++ ++ ++ .global enable_pmn_irq @ export this function for the linker ++ /* Enables interrupt generation on overflow of PMN{x} */ ++ /* void enable_pmn_irq(uint32_t counter) */ ++ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for ++enable_pmn_irq: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter */ ++ MOV r0, r1, LSL r0 ++ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ ++ BX lr ++ ++ .global disable_pmn_irq @ export this function for the linker ++ /* Disables interrupt generation on overflow of PMN{x} */ ++ /* void disable_pmn_irq(uint32_t counter) */ ++ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo ++disable_pmn_irq: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r0, r1, LSL r0 ++ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ ++ BX lr ++ ++ /* --------------------------------------------------------------- */ ++ /* Reset Functions */ ++ /* --------------------------------------------------------------- */ ++ ++ .global reset_pmn @ export this function for the linker ++ /* Resets the programmable counters */ ++ /* void reset_pmn(void) */ ++reset_pmn: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ .global reset_ccnt @ export this function for the linker ++ /* Resets the CCNT */ ++ /* void reset_ccnt(void) */ ++reset_ccnt: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ .end @end of code, this line is optional. ++/* ------------------------------------------------------------ */ ++/* End of v7_pmu.s */ ++/* ------------------------------------------------------------ */ ++ ++ --- /dev/null +++ b/modules/hw/mmal/v7_pmu.h @@ -0,0 +1,113 @@ -+// ------------------------------------------------------------ -+// PMU for Cortex-A/R (v7-A/R) -+// ------------------------------------------------------------ -+ -+#ifndef _V7_PMU_H -+#define _V7_PMU_H -+ -+// Returns the number of progammable counters -+unsigned int getPMN(void); -+ -+// Sets the event for a programmable counter to record -+// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1) -+// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual) -+void pmn_config(unsigned int counter, unsigned int event); -+ -+// Enables/disables the divider (1/64) on CCNT -+// divider = r0 = If 0 disable divider, else enable dvider -+void ccnt_divider(int divider); -+ -+// -+// Enables and disables -+// -+ -+// Global PMU enable -+// On ARM11 this enables the PMU, and the counters start immediately -+// On Cortex this enables the PMU, there are individual enables for the counters -+void enable_pmu(void); -+ -+// Global PMU disable -+// On Cortex, this overrides the enable state of the individual counters -+void disable_pmu(void); -+ -+// Enable the CCNT -+void enable_ccnt(void); -+ -+// Disable the CCNT -+void disable_ccnt(void); -+ -+// Enable PMN{n} -+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) -+void enable_pmn(unsigned int counter); -+ -+// Enable PMN{n} -+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) -+void disable_pmn(unsigned int counter); -+ -+// -+// Read counter values -+// -+ -+// Returns the value of CCNT -+unsigned int read_ccnt(void); -+ -+// Returns the value of PMN{n} -+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1) -+unsigned int read_pmn(unsigned int counter); -+ -+// -+// Overflow and interrupts -+// -+ -+// Returns the value of the overflow flags -+unsigned int read_flags(void); -+ -+// Writes the overflow flags -+void write_flags(unsigned int flags); -+ -+// Enables interrupt generation on overflow of the CCNT -+void enable_ccnt_irq(void); -+ -+// Disables interrupt generation on overflow of the CCNT -+void disable_ccnt_irq(void); -+ -+// Enables interrupt generation on overflow of PMN{x} -+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) -+void enable_pmn_irq(unsigned int counter); -+ -+// Disables interrupt generation on overflow of PMN{x} -+// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) -+void disable_pmn_irq(unsigned int counter); -+ -+// -+// Counter reset functions -+// -+ -+// Resets the programmable counters -+void reset_pmn(void); -+ -+// Resets the CCNT -+void reset_ccnt(void); -+ -+// -+// Software Increment -+ -+// Writes to software increment register -+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1) -+void pmu_software_increment(unsigned int counter); -+ -+// -+// User mode access -+// -+ -+// Enables User mode access to the PMU (must be called in a priviledged mode) -+void enable_pmu_user_access(void); -+ -+// Disables User mode access to the PMU (must be called in a priviledged mode) -+void disable_pmu_user_access(void); -+ -+#endif -+// ------------------------------------------------------------ -+// End of v7_pmu.h -+// ------------------------------------------------------------ -+ ++// ------------------------------------------------------------ ++// PMU for Cortex-A/R (v7-A/R) ++// ------------------------------------------------------------ ++ ++#ifndef _V7_PMU_H ++#define _V7_PMU_H ++ ++// Returns the number of progammable counters ++unsigned int getPMN(void); ++ ++// Sets the event for a programmable counter to record ++// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1) ++// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual) ++void pmn_config(unsigned int counter, unsigned int event); ++ ++// Enables/disables the divider (1/64) on CCNT ++// divider = r0 = If 0 disable divider, else enable dvider ++void ccnt_divider(int divider); ++ ++// ++// Enables and disables ++// ++ ++// Global PMU enable ++// On ARM11 this enables the PMU, and the counters start immediately ++// On Cortex this enables the PMU, there are individual enables for the counters ++void enable_pmu(void); ++ ++// Global PMU disable ++// On Cortex, this overrides the enable state of the individual counters ++void disable_pmu(void); ++ ++// Enable the CCNT ++void enable_ccnt(void); ++ ++// Disable the CCNT ++void disable_ccnt(void); ++ ++// Enable PMN{n} ++// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++void enable_pmn(unsigned int counter); ++ ++// Enable PMN{n} ++// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++void disable_pmn(unsigned int counter); ++ ++// ++// Read counter values ++// ++ ++// Returns the value of CCNT ++unsigned int read_ccnt(void); ++ ++// Returns the value of PMN{n} ++// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1) ++unsigned int read_pmn(unsigned int counter); ++ ++// ++// Overflow and interrupts ++// ++ ++// Returns the value of the overflow flags ++unsigned int read_flags(void); ++ ++// Writes the overflow flags ++void write_flags(unsigned int flags); ++ ++// Enables interrupt generation on overflow of the CCNT ++void enable_ccnt_irq(void); ++ ++// Disables interrupt generation on overflow of the CCNT ++void disable_ccnt_irq(void); ++ ++// Enables interrupt generation on overflow of PMN{x} ++// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) ++void enable_pmn_irq(unsigned int counter); ++ ++// Disables interrupt generation on overflow of PMN{x} ++// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) ++void disable_pmn_irq(unsigned int counter); ++ ++// ++// Counter reset functions ++// ++ ++// Resets the programmable counters ++void reset_pmn(void); ++ ++// Resets the CCNT ++void reset_ccnt(void); ++ ++// ++// Software Increment ++ ++// Writes to software increment register ++// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1) ++void pmu_software_increment(unsigned int counter); ++ ++// ++// User mode access ++// ++ ++// Enables User mode access to the PMU (must be called in a priviledged mode) ++void enable_pmu_user_access(void); ++ ++// Disables User mode access to the PMU (must be called in a priviledged mode) ++void disable_pmu_user_access(void); ++ ++#endif ++// ------------------------------------------------------------ ++// End of v7_pmu.h ++// ------------------------------------------------------------ ++ --- a/modules/hw/mmal/vout.c +++ b/modules/hw/mmal/vout.c @@ -27,21 +27,24 @@ @@ -8170,8 +9314,7 @@ -static int Open(vlc_object_t *); -static void Close(vlc_object_t *); -+#define SUBS_MAX 4 - +- -vlc_module_begin() - set_shortname(N_("MMAL vout")) - set_description(N_("MMAL-based vout plugin for Raspberry Pi")) @@ -8186,7 +9329,8 @@ - MMAL_NATIVE_INTERLACE_LONGTEXT, false) - set_callbacks(Open, Close) -vlc_module_end() -- ++#define SUBS_MAX 4 + -struct dmx_region_t { - struct dmx_region_t *next; - picture_t *picture; @@ -8213,13 +9357,14 @@ - picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */ - picture_pool_t *picture_pool; - ++ vcsm_init_type_t init_type; MMAL_COMPONENT_T *component; MMAL_PORT_T *input; MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/ - struct dmx_region_t *dmx_region; int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */ - uint32_t buffer_size; /* size of actual mmal buffers */ +- uint32_t buffer_size; /* size of actual mmal buffers */ int buffers_in_transit; /* number of buffers currently pushed to mmal component */ unsigned num_buffers; /* number of buffers allocated at mmal port */ @@ -8236,7 +9381,7 @@ int next_phase_check; /* lowpass for phase check frequency */ int phase_offset; /* currently applied offset to presentation time in ns */ -@@ -136,264 +100,415 @@ +@@ -136,264 +100,451 @@ bool native_interlaced; bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ bool b_progressive; @@ -8247,7 +9392,11 @@ -static const vlc_fourcc_t subpicture_chromas[] = { - VLC_CODEC_RGBA, - 0 +-}; + vout_subpic_t subs[SUBS_MAX]; ++ // Stash for subpics derived from the passed subpicture rather than ++ // included with the main pic ++ MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX]; + + picture_pool_t * pic_pool; + @@ -8260,12 +9409,13 @@ + MMAL_POOL_T * out_pool; + bool pending; + } isp; - }; -/* Utility functions */ -static inline uint32_t align(uint32_t x, uint32_t y); -static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, - const video_format_t *fmt); ++ MMAL_POOL_T * copy_pool; ++ MMAL_BUFFER_HEADER_T * copy_buf; -/* VLC vout display callbacks */ -static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count); @@ -8279,7 +9429,9 @@ -/* MMAL callbacks */ -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); -+// ISP setup ++ // Subpic blend if we have to do it here ++ vzc_pool_ctl_t * vzc; ++}; -/* TV service */ -static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height); @@ -8287,10 +9439,6 @@ - uint32_t param2); -static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); -static int set_latency_target(vout_display_t *vd, bool enable); -+static inline bool want_isp(const vout_display_t * const vd) -+{ -+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); -+} -/* DispManX */ -static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); @@ -8303,6 +9451,56 @@ - DISPMANX_UPDATE_HANDLE_T update); -static void show_background(vout_display_t *vd, bool enable); -static void maintain_phase_sync(vout_display_t *vd); ++// ISP setup + +-static int Open(vlc_object_t *object) ++static inline bool want_isp(const vout_display_t * const vd) + { +- vout_display_t *vd = (vout_display_t *)object; +- vout_display_sys_t *sys; +- uint32_t buffer_pitch, buffer_height; +- vout_display_place_t place; +- MMAL_DISPLAYREGION_T display_region; +- MMAL_STATUS_T status; +- int ret = VLC_SUCCESS; +- unsigned i; ++ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); ++} + +- if (vout_display_IsWindowed(vd)) +- return VLC_EGENERIC; ++static inline bool want_copy(const vout_display_t * const vd) ++{ ++ return (vd->fmt.i_chroma == VLC_CODEC_I420); ++} + +- sys = calloc(1, sizeof(struct vout_display_sys_t)); +- if (!sys) +- return VLC_ENOMEM; +- vd->sys = sys; ++// Do a stride converting copy - if the strides are the same and line_len is ++// close then do a single block copy - we don't expect to have to preserve ++// pixels in the output frame ++static inline void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride, const uint8_t * s_ptr, const size_t s_stride, size_t lines, const size_t line_len) ++{ ++ if (s_stride == d_stride && s_stride < line_len + 32) ++ { ++ memcpy(d_ptr, s_ptr, s_stride * lines); ++ } ++ else ++ { ++ while (lines-- != 0) { ++ memcpy(d_ptr, s_ptr, line_len); ++ d_ptr += d_stride; ++ s_ptr += s_stride; ++ } ++ } ++} + +- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); +- bcm_host_init(); + +- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; +static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) +{ + switch (fcc){ @@ -8312,23 +9510,22 @@ + return MMAL_ENCODING_YUVUV128; + case VLC_CODEC_MMAL_ZC_SAND10: + return MMAL_ENCODING_YUVUV64_10; // It will be after we've converted it... ++ case VLC_CODEC_I420: ++ return MMAL_ENCODING_I420; + default: + break; + } + return 0; +} --static int Open(vlc_object_t *object) +- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", +- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; +static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) - { -- vout_display_t *vd = (vout_display_t *)object; -- vout_display_sys_t *sys; -- uint32_t buffer_pitch, buffer_height; -- vout_display_place_t place; -- MMAL_DISPLAYREGION_T display_region; -- MMAL_STATUS_T status; -- int ret = VLC_SUCCESS; -- unsigned i; ++{ + const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ; + const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height; + MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; @@ -8349,14 +9546,19 @@ + } else { + v_fmt->par.num = vd->fmt.i_sar_num; + v_fmt->par.den = vd->fmt.i_sar_den; -+ } + } + v_fmt->frame_rate.num = vd->fmt.i_frame_rate; + v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base; + v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space); +} -- if (vout_display_IsWindowed(vd)) -- return VLC_EGENERIC; +- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; +- status = mmal_port_enable(sys->component->control, control_port_cb); +- if (status != MMAL_SUCCESS) { +- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", +- sys->component->control->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; +static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect) +{ + const bool wants_isp = want_isp(vd); @@ -8365,11 +9567,7 @@ + rect->width = vd->fmt.i_visible_width; + rect->height = vd->fmt.i_visible_height; +} - -- sys = calloc(1, sizeof(struct vout_display_sys_t)); -- if (!sys) -- return VLC_ENOMEM; -- vd->sys = sys; ++ +static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ +#if TRACE_ALL @@ -8380,36 +9578,19 @@ +#else + VLC_UNUSED(port); +#endif - -- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); -- bcm_host_init(); ++ + mmal_buffer_header_release(buf); - -- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; ++ +#if TRACE_ALL + msg_Dbg(vd, ">>> %s", __func__); +#endif +} - -- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", -- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; -- } ++ +static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + vout_display_t *vd = (vout_display_t *)port->userdata; + MMAL_STATUS_T status; - -- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; -- status = mmal_port_enable(sys->component->control, control_port_cb); -- if (status != MMAL_SUCCESS) { -- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", -- sys->component->control->name, status, mmal_status_to_string(status)); -- ret = VLC_EGENERIC; -- goto out; ++ + if (buffer->cmd == MMAL_EVENT_ERROR) { + status = *(uint32_t *)buffer->data; + msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); @@ -8846,7 +10027,7 @@ if (fmt) { sys->input->format->es->video.par.num = fmt->i_sar_num; -@@ -412,22 +527,29 @@ +@@ -412,22 +563,29 @@ if (!cfg) cfg = vd->cfg; @@ -8882,7 +10063,7 @@ status = mmal_port_parameter_set(sys->input, &display_region.hdr); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", -@@ -435,7 +557,6 @@ +@@ -435,7 +593,6 @@ return -EINVAL; } @@ -8890,7 +10071,7 @@ sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME); sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED); if (sys->adjust_refresh_rate) { -@@ -446,191 +567,130 @@ +@@ -446,192 +603,153 @@ return 0; } @@ -8970,8 +10151,8 @@ - msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", - sys->component->name, status, mmal_status_to_string(status)); - goto out; -+ // Not expecting subpictures in the current setup -+ // Subpics should be attached to the main pic ++ // If we had subpics then we have attached them to the main pic in prepare ++ // so all we have to do here is delete the refs + if (subpicture != NULL) { + subpicture_Delete(subpicture); } @@ -9005,16 +10186,16 @@ + } + // Stuff into input + // We assume the BH is already set up with values reflecting pic date etc. -+ if (sys->isp.pending) { -+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); -+ sys->isp.pending = false; ++ if (sys->copy_buf != NULL) { ++ MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf; ++ sys->copy_buf = NULL; +#if TRACE_ALL -+ msg_Dbg(vd, "--- %s: ISP stuff", __func__); ++ msg_Dbg(vd, "--- %s: Copy stuff", __func__); +#endif + if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) + { + mmal_buffer_header_release(buf); -+ msg_Err(vd, "Send ISP buffer to render input failed"); ++ msg_Err(vd, "Send copy buffer to render input failed"); + goto fail; + } } @@ -9031,22 +10212,23 @@ - msg_Err(vd, "Failed to create picture"); - free(picture_res.p_sys); - goto out; -+ else -+ { -+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); ++ else if (sys->isp.pending) { ++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); ++ sys->isp.pending = false; +#if TRACE_ALL -+ msg_Dbg(vd, "--- %s: Buf stuff", __func__); ++ msg_Dbg(vd, "--- %s: ISP stuff", __func__); +#endif -+ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS) ++ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) + { -+ msg_Err(vd, "Send buffer to input failed"); ++ mmal_buffer_header_release(buf); ++ msg_Err(vd, "Send ISP buffer to render input failed"); + goto fail; } - - sys->pictures[i]->i_planes = sys->i_planes; - memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t)); } - +- - memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t)); - picture_pool_cfg.picture_count = sys->num_buffers; - picture_pool_cfg.picture = sys->pictures; @@ -9056,12 +10238,18 @@ - if (!sys->picture_pool) { - msg_Err(vd, "Failed to create picture pool"); - goto out; -+ if (p_pic->context == NULL) { -+ msg_Dbg(vd, "%s: No context", __func__); - } + else + { -+ unsigned int sub_no = 0; ++ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); ++#if TRACE_ALL ++ msg_Dbg(vd, "--- %s: Buf stuff", __func__); ++#endif ++ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS) ++ { ++ msg_Err(vd, "Send buffer to input failed"); ++ goto fail; ++ } + } -out: - return sys->picture_pool; @@ -9112,9 +10300,20 @@ - if (status != MMAL_SUCCESS) { - msg_Err(vd, "Failed to send buffer to input port. Frame dropped"); - picture_Release(picture); ++ { ++ unsigned int sub_no = 0; ++ MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs; ++ const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic); ++ + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { + int rv; -+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), p_pic, sub_no, &sys->subs[sub_no].sub, ++ MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL : ++ hw_mmal_pic_sub_buf_get(p_pic, sub_no); ++ ++ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), ++ sub_buf != NULL ? sub_buf : *psub_bufs2++, ++ &sys->subs[sub_no].sub, ++ &p_pic->format, + &(MMAL_RECT_T){.width = sys->display_width, .height = sys->display_height}, + p_pic->date)) == 0) + break; @@ -9128,7 +10327,12 @@ } - display_subpicture(vd, subpicture); -- ++fail: ++ for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) { ++ mmal_buffer_header_release(sys->subpic_bufs[i]); ++ sys->subpic_bufs[i] = NULL; ++ } + - if (subpicture) - subpicture_Delete(subpicture); + picture_Release(p_pic); @@ -9136,15 +10340,13 @@ if (sys->next_phase_check == 0 && sys->adjust_refresh_rate) maintain_phase_sync(vd); sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL; - +- - if (sys->opaque) { - vlc_mutex_lock(&sys->buffer_mutex); - while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT) - vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex); - vlc_mutex_unlock(&sys->buffer_mutex); - } -+fail: -+ /* NOP */; } static int vd_control(vout_display_t *vd, int query, va_list args) @@ -9167,12 +10369,15 @@ - if (configure_display(vd, &cfg, NULL) >= 0) - ret = VLC_SUCCESS; - } ++ { + // Ignore this - we just use full screen anyway + ret = VLC_SUCCESS; break; ++ } case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: -@@ -640,10 +700,37 @@ + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: +@@ -640,11 +758,38 @@ break; case VOUT_DISPLAY_RESET_PICTURES: @@ -9186,8 +10391,8 @@ case VOUT_DISPLAY_CHANGE_ZOOM: msg_Warn(vd, "Unsupported control query %d", query); + ret = VLC_SUCCESS; -+ break; -+ + break; + + case VOUT_DISPLAY_CHANGE_MMAL_HIDE: + { + MMAL_STATUS_T err; @@ -9206,12 +10411,13 @@ + } + sys->force_config = true; + ret = VLC_SUCCESS; - break; ++ break; + } - ++ default: msg_Warn(vd, "Unknown control query %d", query); -@@ -661,13 +748,11 @@ + break; +@@ -661,13 +806,11 @@ vlc_mutex_lock(&sys->manage_mutex); if (sys->need_configure_display) { @@ -9227,11 +10433,69 @@ } sys->need_configure_display = false; -@@ -676,56 +761,76 @@ +@@ -676,56 +819,164 @@ vlc_mutex_unlock(&sys->manage_mutex); } -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) ++ ++static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys, ++ subpicture_t * const subpicture) + { +- vout_display_t *vd = (vout_display_t *)port->userdata; +- MMAL_STATUS_T status; ++ unsigned int n = 0; + +- if (buffer->cmd == MMAL_EVENT_ERROR) { +- status = *(uint32_t *)buffer->data; +- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); ++ if (sys->vzc == NULL) { ++ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) ++ { ++ msg_Err(vd, "Failed to allocate VZC"); ++ return VLC_ENOMEM; ++ } + } + +- mmal_buffer_header_release(buffer); ++ // Attempt to import the subpics ++ for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next) ++ { ++ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) { ++ picture_t *const src = sreg->p_picture; ++ ++#if 0 ++ char dbuf0[5]; ++ msg_Dbg(vd, " [%p:%p] Pos=%d,%d src=%dx%d/%dx%d, vd=%dx%d/%dx%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels, ++ sreg->i_x, sreg->i_y, ++ src->format.i_visible_width, src->format.i_visible_height, ++ src->format.i_width, src->format.i_height, ++ vd->fmt.i_visible_width, vd->fmt.i_visible_height, ++ vd->fmt.i_width, vd->fmt.i_height, ++ sreg->i_alpha, ++ str_fourcc(dbuf0, src->format.i_chroma)); ++#endif ++ ++ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc, ++ src, ++ (MMAL_RECT_T){.width = vd->cfg->display.width, .height=vd->cfg->display.height}, ++ sreg->i_x, sreg->i_y, ++ sreg->i_alpha, ++ n == 0)) == NULL) ++ { ++ msg_Err(vd, "Failed to allocate vzc buffer for subpic"); ++ return VLC_ENOMEM; ++ } ++ ++ if (++n == SUBS_MAX) ++ return VLC_SUCCESS; ++ } ++ } ++ return VLC_SUCCESS; + } + +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) ++ +static void vd_prepare(vout_display_t *vd, picture_t *p_pic, +#if VLC_VER_3 + subpicture_t *subpicture @@ -9241,24 +10505,55 @@ + ) { - vout_display_t *vd = (vout_display_t *)port->userdata; -- MMAL_STATUS_T status; + MMAL_STATUS_T err; + vout_display_sys_t * const sys = vd->sys; - -- if (buffer->cmd == MMAL_EVENT_ERROR) { -- status = *(uint32_t *)buffer->data; -- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); -+ VLC_UNUSED(subpicture); -+// VLC_UNUSED(date); + + vd_manage(vd); + ++ // Subpics can either turn up attached to the main pic or in the ++ // subpic list here - if they turn up here then process inrto temp ++ // buffers ++ if (subpicture != NULL) { ++ attach_subpics(vd, sys, subpicture); ++ } ++ ++ // ***** ++ if (want_copy(vd)) { ++ if (sys->copy_buf != NULL) { ++ msg_Err(vd, "Copy buf not NULL"); ++ mmal_buffer_header_release(sys->copy_buf); ++ sys->copy_buf = NULL; ++ } ++ ++ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue); ++ // Copy 2d ++ unsigned int y_size = sys->input->format->es->video.width * sys->input->format->es->video.height; ++ ++ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; ++ buf->length = sys->input->buffer_size; ++ ++ mem_copy_2d(buf->data, sys->input->format->es->video.width, ++ p_pic->p[0].p_pixels, p_pic->p[0].i_pitch, ++ sys->input->format->es->video.crop.height, ++ sys->input->format->es->video.crop.width); ++ ++ mem_copy_2d(buf->data + y_size, sys->input->format->es->video.width / 2, ++ p_pic->p[1].p_pixels, p_pic->p[1].i_pitch, ++ sys->input->format->es->video.crop.height / 2, ++ sys->input->format->es->video.crop.width / 2); ++ ++ mem_copy_2d(buf->data + y_size + y_size / 4, sys->input->format->es->video.width / 2, ++ p_pic->p[2].p_pixels, p_pic->p[2].i_pitch, ++ sys->input->format->es->video.crop.height / 2, ++ sys->input->format->es->video.crop.width / 2); ++ ++ sys->copy_buf = buf; ++ } ++ + if (isp_check(vd, sys) != MMAL_SUCCESS) { + return; - } - -- mmal_buffer_header_release(buffer); --} ++ } ++ + if (want_isp(vd)) + { + struct vout_isp_conf_s * const isp = &sys->isp; @@ -9267,10 +10562,7 @@ + // This should be empty - make it so if it isn't + isp_empty_out_q(isp); + isp->pending = false; - --static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) --{ -- vout_display_t *vd = (vout_display_t *)port->userdata; ++ + // Stuff output + if (isp_prepare(vd, isp) != MMAL_SUCCESS) + return; @@ -9340,7 +10632,7 @@ } static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) -@@ -828,148 +933,12 @@ +@@ -828,148 +1079,12 @@ } } @@ -9490,7 +10782,7 @@ ((double)vd->sys->i_frame_rate / vd->sys->i_frame_rate_base); vout_display_sys_t *sys = vd->sys; -@@ -1012,32 +981,260 @@ +@@ -1012,32 +1127,286 @@ } } @@ -9534,28 +10826,36 @@ + + vc_tv_unregister_callback_full(tvservice_cb, vd); + -+ if (sys->component && sys->component->control->is_enabled) -+ mmal_port_disable(sys->component->control); -+ -+ { -+ unsigned int i; -+ for (i = 0; i != SUBS_MAX; ++i) { -+ vout_subpic_t * const sub = sys->subs + i; -+ if (sub->component != NULL) { -+ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub); -+ if (sub->component->control->is_enabled) -+ mmal_port_disable(sub->component->control); -+ if (sub->component->is_enabled) -+ mmal_component_disable(sub->component); -+ mmal_component_release(sub->component); -+ sub->component = NULL; -+ } ++ // Shouldn't be anything here - but just in case ++ for (unsigned int i = 0; i != SUBS_MAX; ++i) ++ if (sys->subpic_bufs[i] != NULL) ++ mmal_buffer_header_release(sys->subpic_bufs[i]); ++ ++ for (unsigned int i = 0; i != SUBS_MAX; ++i) { ++ vout_subpic_t * const sub = sys->subs + i; ++ if (sub->component != NULL) { ++ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub); ++ if (sub->component->control->is_enabled) ++ mmal_port_disable(sub->component->control); ++ if (sub->component->is_enabled) ++ mmal_component_disable(sub->component); ++ mmal_component_release(sub->component); ++ sub->component = NULL; + } } + + if (sys->input && sys->input->is_enabled) + mmal_port_disable(sys->input); + ++ if (sys->component && sys->component->control->is_enabled) ++ mmal_port_disable(sys->component->control); ++ ++ if (sys->copy_buf != NULL) ++ mmal_buffer_header_release(sys->copy_buf); ++ ++ if (sys->input != NULL && sys->copy_pool != NULL) ++ mmal_port_pool_destroy(sys->input, sys->copy_pool); ++ + if (sys->component && sys->component->is_enabled) + mmal_component_disable(sys->component); + @@ -9567,6 +10867,8 @@ + + isp_close(vd, sys); + ++ hw_mmal_vzc_pool_release(sys->vzc); ++ + vlc_mutex_destroy(&sys->manage_mutex); + + if (sys->native_interlaced) { @@ -9575,14 +10877,14 @@ + msg_Warn(vd, "Could not reset hvs field mode"); + } + -+ free(sys); ++ cma_vcsm_exit(sys->init_type);; + -+ bcm_host_deinit(); ++ free(sys); + +#if TRACE_ALL + msg_Dbg(vd, ">>> %s", __func__); +#endif - } ++} + +static int OpenMmalVout(vlc_object_t *object) +{ @@ -9600,7 +10902,8 @@ + if (enc_in == 0) + { +#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: Format not MMAL", __func__); ++ char dbuf0[5]; ++ msg_Dbg(vd, ">>> %s: Format %s not MMAL", __func__, str_fourcc(dbuf0, vd->fmt.i_chroma)); +#endif + return VLC_EGENERIC; + } @@ -9610,7 +10913,11 @@ + return VLC_ENOMEM; + vd->sys = sys; + -+ bcm_host_init(); ++ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE) ++ { ++ msg_Err(vd, "VCSM init fail"); ++ goto fail; ++ } + + sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); + @@ -9635,7 +10942,6 @@ + sys->input->format->encoding = enc_in; + sys->input->format->encoding_variant = 0; + sys->i_planes = 1; -+ sys->buffer_size = sys->input->buffer_size_recommended; + + display_set_format(vd, sys->input->format, want_isp(vd)); + @@ -9652,8 +10958,20 @@ + sys->input->name, status, mmal_status_to_string(status)); + goto fail; + } ++ + sys->input->buffer_size = sys->input->buffer_size_recommended; -+ sys->input->buffer_num = 30; ++ ++ if (!want_copy(vd)) { ++ sys->input->buffer_num = 30; ++ } ++ else { ++ sys->input->buffer_num = 2; ++ if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL) ++ { ++ msg_Err(vd, "Cannot create copy pool"); ++ goto fail; ++ } ++ } + + vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); + display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; @@ -9729,7 +11047,7 @@ + .has_double_click = false, + .needs_hide_mouse = false, + .has_pictures_invalid = true, -+ .subpicture_chromas = NULL ++ .subpicture_chromas = hw_mmal_vzc_subpicture_chromas + }; + + vd->pool = vd_pool; @@ -9754,7 +11072,7 @@ + msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret); + + return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; -+} + } + +vlc_module_begin() + @@ -9762,7 +11080,7 @@ + + set_shortname(N_("MMAL vout")) + set_description(N_("MMAL-based vout plugin for Raspberry Pi")) -+ set_capability("vout display", 0) ++ set_capability("vout display", 16) // 1 point better than ASCII art + add_shortcut("mmal_vout") + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VOUT ) @@ -9779,7 +11097,7 @@ + --- /dev/null +++ b/modules/hw/mmal/xsplitter.c -@@ -0,0 +1,403 @@ +@@ -0,0 +1,437 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif @@ -9808,21 +11126,9 @@ + vout_display_t * mmal_vout; + vout_display_t * x_vout; + uint32_t changed; ++ vlc_fourcc_t subpicture_chromas[16]; +} mmal_x11_sys_t; + -+static const char * str_fourcc(char * buf, unsigned int fcc) -+{ -+ if (fcc == 0) -+ return "----"; -+ buf[0] = (fcc >> 0) & 0xff; -+ buf[1] = (fcc >> 8) & 0xff; -+ buf[2] = (fcc >> 16) & 0xff; -+ buf[3] = (fcc >> 24) & 0xff; -+ buf[4] = 0; -+ return buf; -+} -+ -+ +static void unload_display_module(vout_display_t * const x_vout) +{ + if (x_vout != NULL) { @@ -9858,6 +11164,12 @@ +#if TRACE_ALL + msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd); +#endif ++ ++ // Do not fall into the display assert if Invalid not supported ++ if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID && ++ !vd->info.has_pictures_invalid) ++ return; ++ + vd->owner.event(vd, cmd, args); +} + @@ -9985,7 +11297,10 @@ + is_mmal_pic, sys->use_mmal); +#endif + -+ if (sys->use_mmal != is_mmal_pic) { ++ if (x_vd->fmt.i_chroma != pic->format.i_chroma || ++ x_vd->fmt.i_width != pic->format.i_width || ++ x_vd->fmt.i_height != pic->format.i_height) ++ { + msg_Dbg(vd, "%s: Picture dropped", __func__); + picture_Release(pic); + if (sub != NULL) @@ -10011,7 +11326,7 @@ + +static bool want_mmal_vout(vout_display_t * vd, const mmal_x11_sys_t * const sys) +{ -+ return sys->mmal_vout != NULL && var_InheritBool(vd, "fullscreen"); ++ return sys->mmal_vout != NULL && (sys->x_vout == NULL || var_InheritBool(vd, "fullscreen")); +} + +/* Control on the module (mandatory) */ @@ -10030,19 +11345,20 @@ + switch (ctl) { + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: + { -+ const vout_display_cfg_t * cfg = va_arg(va, const vout_display_cfg_t *); ++ const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *); + const bool want_mmal = want_mmal_vout(vd, sys); -+ vout_display_t *new_vd = want_mmal ? sys->mmal_vout : sys->x_vout; ++ const bool swap_vout = (sys->use_mmal != want_mmal); ++ vout_display_t * const new_vd = want_mmal ? sys->mmal_vout : sys->x_vout; + + msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d", + cfg->display.width, cfg->display.height, sys->mmal_vout, want_mmal, + var_InheritBool(vd, "fullscreen")); + -+ if (sys->use_mmal != want_mmal) { ++ if (swap_vout) { + if (sys->use_mmal) { + vout_display_Control(x_vd, VOUT_DISPLAY_CHANGE_MMAL_HIDE); + } -+ vout_display_SendEventPicturesInvalid(x_vd); ++ vout_display_SendEventPicturesInvalid(vd); + } + + rv = vout_display_Control(new_vd, ctl, cfg); @@ -10053,7 +11369,7 @@ + } + + // Repeat any control calls that we sent to the previous vd -+ if (sys->changed != 0) { ++ if (swap_vout && sys->changed != 0) { + const uint32_t changed = sys->changed; + sys->changed = 0; + if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0) @@ -10074,20 +11390,24 @@ + } + + case VOUT_DISPLAY_RESET_PICTURES: -+ msg_Dbg(vd, "Reset pictures"); -+// rv = x_vd->control(x_vd, ctl, va); -+ rv = sys->x_vout->control(sys->x_vout, ctl, va); -+ if (sys->mmal_vout) -+ rv = sys->mmal_vout->control(sys->mmal_vout, ctl, va); + msg_Dbg(vd, "<<< %s: Pic reset: fmt: %dx%d<-%dx%d, source: %dx%d/%dx%d", __func__, + vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, + vd->source.i_width, vd->source.i_height, x_vd->source.i_width, x_vd->source.i_height); -+ vd->fmt = x_vd->fmt; ++ // If the display doesn't have has_pictures_invalid then it doesn't ++ // expect RESET_PICTURES ++ if (sys->x_vout->info.has_pictures_invalid) { ++ rv = sys->x_vout->control(sys->x_vout, ctl, va); ++ } ++ if (sys->mmal_vout && sys->mmal_vout->info.has_pictures_invalid) { ++ rv = sys->mmal_vout->control(sys->mmal_vout, ctl, va); ++ } ++ vd->fmt = x_vd->fmt; + break; + + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: + x_vd->source = vd->source; ++ /* FALLTHRU */ + default: + rv = x_vd->control(x_vd, ctl, va); +// vd->fmt = x_vd->fmt; @@ -10133,14 +11453,18 @@ + .subpicture_chromas = NULL + }; + -+ if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) == NULL) -+ goto fail; ++ if ((sys->x_vout = load_display_module(vd, "vout display", "opengles2")) != NULL) ++ msg_Dbg(vd, "Opengles2 output found"); ++ else if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) != NULL) ++ msg_Dbg(vd, "X11 XCB output found"); + -+ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) == NULL) -+ { -+ // Winge but do no more than that - route everything to X ++ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) != NULL) ++ msg_Dbg(vd, "MMAL output found"); ++ ++ if (sys->mmal_vout == NULL && sys->x_vout == NULL) { + char dbuf0[5], dbuf1[5]; -+ msg_Info(vd, "Not a valid format for mmal vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma)); ++ msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma)); ++ goto fail; + } + + vd->pool = mmal_x11_pool; @@ -10160,7 +11484,35 @@ + sys->use_mmal = false; + } + -+ vd->info = sys->cur_vout->info; ++ if (sys->mmal_vout == NULL || sys->x_vout == NULL) { ++ vd->info = sys->cur_vout->info; ++ vd->info.has_pictures_invalid = true; // Should make this unwanted ++ } ++ else { ++ // We have both - construct a combination ++ vd->info = (vout_display_info_t){ ++ .is_slow = false, ++ .has_double_click = sys->mmal_vout->info.has_double_click || sys->x_vout->info.has_double_click, ++ .needs_hide_mouse = sys->mmal_vout->info.needs_hide_mouse || sys->x_vout->info.needs_hide_mouse, ++ .has_pictures_invalid = true, ++ }; ++ // Construct intersection of subpicture chromas ++ // sys calloced so no need to add the terminating zero ++ if (sys->mmal_vout->info.subpicture_chromas != NULL && sys->x_vout->info.subpicture_chromas != NULL) { ++ unsigned int n = 0; ++ // N^2 - fix if we ever care ++ for (const vlc_fourcc_t * p1 = sys->mmal_vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) { ++ for (const vlc_fourcc_t * p2 = sys->x_vout->info.subpicture_chromas; *p2 != 0; ++p2) { ++ if (*p1 == *p2) { ++ sys->subpicture_chromas[n++] = *p1; ++ break; ++ } ++ } ++ } ++ if (n != 0) ++ vd->info.subpicture_chromas = sys->subpicture_chromas; ++ } ++ } + vd->fmt = sys->cur_vout->fmt; + + return VLC_SUCCESS; @@ -10176,21 +11528,53 @@ +vlc_module_begin() + set_shortname(N_("MMAL x11 splitter")) + set_description(N_("MMAL x11 splitter for Raspberry Pi")) -+ set_capability("vout display", 900) ++ set_capability("vout display", 300) // Between GLES & GL + add_shortcut("mmal_x11") + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VOUT ) + set_callbacks(OpenMmalX11, CloseMmalX11) +vlc_module_end() + +--- a/modules/video_output/opengl/egl.c ++++ b/modules/video_output/opengl/egl.c +@@ -43,6 +43,8 @@ + # include "../android/utils.h" + #endif + ++#define REQUIRE_DMA_BUF_IMPORT 1 ++ + typedef struct vlc_gl_sys_t + { + EGLDisplay display; +@@ -354,6 +356,14 @@ + goto error; + } + ++#if REQUIRE_DMA_BUF_IMPORT ++ if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import")) ++ { ++ msg_Dbg(obj, "No dma_buf_import - fall back to X"); ++ goto error; ++ } ++#endif ++ + const EGLint conf_attr[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 5, --- a/src/misc/fourcc.c +++ b/src/misc/fourcc.c -@@ -756,6 +756,8 @@ +@@ -755,8 +755,12 @@ + { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422, VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, FAKE_FMT() }, - { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, -+ VLC_CODEC_MMAL_ZC_SAND8, VLC_CODEC_MMAL_ZC_SAND10, -+ VLC_CODEC_MMAL_ZC_I420, - VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, +- { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, +- VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, ++ { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE }, ++ FAKE_FMT() }, ++ { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8, ++ VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 }, ++ FAKE_FMT() }, ++ { { VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, FAKE_FMT() }, { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B }, + FAKE_FMT() },