// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_AMLOGIC_MEDIA_FB #include #endif #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM #include #endif #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION #include #else #include #endif #include #include #include "meson_plane.h" #include "meson_crtc.h" #include "meson_vpu.h" #include "meson_drv.h" #include "meson_vpu_pipeline.h" #include "vpu-hw/meson_osd_afbc.h" #include "meson_gem.h" #include "meson_logo.h" static const struct drm_plane_funcs am_video_plane_funs; static int video_axis_zoom = -1; module_param(video_axis_zoom, int, 0664); MODULE_PARM_DESC(video_axis_zoom, "video_axis_zoom"); static int bypass_video_crop; module_param(bypass_video_crop, int, 0664); MODULE_PARM_DESC(bypass_video_crop, "bypass_video_crop"); static int osd_enable[MESON_MAX_OSDS]; static u64 afbc_modifier[] = { /* * - TOFIX Support AFBC modifiers for YUV formats (16x16 + TILED) * - SPLIT is mandatory for performances reasons when in 16x16 * block size * - 32x8 block size + SPLIT is mandatory with 4K frame size * for performances reasons */ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC | AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 | AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC | AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_SPLIT), /* - AFRC * - now just support scanline layout, we need notify encoder this flag * - by add AFRC_FORMAT_MOD_LAYOUT_SCAN explicitly. */ DRM_FORMAT_MOD_ARM_AFRC(AFRC_FORMAT_MOD_CU_SIZE_16 | AFRC_FORMAT_MOD_LAYOUT_SCAN), DRM_FORMAT_MOD_ARM_AFRC(AFRC_FORMAT_MOD_CU_SIZE_24 | AFRC_FORMAT_MOD_LAYOUT_SCAN), DRM_FORMAT_MOD_ARM_AFRC(AFRC_FORMAT_MOD_CU_SIZE_32 | AFRC_FORMAT_MOD_LAYOUT_SCAN), DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID }; /* default groups */ u32 supported_drm_formats[] = { DRM_FORMAT_ABGR2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_RGB565, }; /* default groups + 1010102 formats */ u32 supported_drm_formats_v2[] = { DRM_FORMAT_RGBA1010102, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, DRM_FORMAT_BGRA1010102, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_RGB565, }; u32 supported_drm_formats_v6[] = { DRM_FORMAT_RGBA1010102, DRM_FORMAT_RGBX1010102, DRM_FORMAT_ABGR2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ARGB2101010, DRM_FORMAT_BGRA1010102, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_RGB565, DRM_FORMAT_ARGB4444, DRM_FORMAT_RGBA4444, DRM_FORMAT_RGBX4444, DRM_FORMAT_XRGB4444, DRM_FORMAT_RGBA5551, DRM_FORMAT_ARGB1555, DRM_FORMAT_RGBX5551, DRM_FORMAT_XRGB1555, DRM_FORMAT_C8, }; /* v2 groups + 4444 formats */ u32 supported_drm_formats_v3[] = { DRM_FORMAT_RGBA1010102, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010, DRM_FORMAT_BGRA1010102, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_ABGR4444, DRM_FORMAT_ARGB4444, DRM_FORMAT_BGRA4444, DRM_FORMAT_RGBA4444, DRM_FORMAT_RGB565, }; /* formats supported by s1a */ u32 supported_drm_formats_v4[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_RGB565, DRM_FORMAT_RGBA4444, DRM_FORMAT_ARGB4444, DRM_FORMAT_RGBA5551, DRM_FORMAT_ARGB1555, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_RGBA8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGBA1010102, DRM_FORMAT_ABGR2101010, DRM_FORMAT_C8, }; /* default groups + 10101010 format for s7d*/ u32 supported_drm_formats_v5[] = { DRM_FORMAT_ABGR10101010, DRM_FORMAT_ABGR2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRX8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_RGB888, DRM_FORMAT_BGR888, DRM_FORMAT_RGB565, }; static u64 video_fbc_modifier[] = { DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, 0), DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_BASIC, AMLOGIC_FBC_OPTION_MEM_SAVING), DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, 0), DRM_FORMAT_MOD_AMLOGIC_FBC(AMLOGIC_FBC_LAYOUT_SCATTER, AMLOGIC_FBC_OPTION_MEM_SAVING), DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID }; u32 video_supported_drm_formats[] = { DRM_FORMAT_NV12, DRM_FORMAT_NV21, DRM_FORMAT_UYVY, DRM_FORMAT_VUY888, DRM_FORMAT_YUVX1010102, }; static void osd_plane_mute(bool mute) { /* to do */ DRM_DEBUG_DRIVER("mute osd plane.\n"); } static void video_plane_mute(bool mute) { DRM_DEBUG_DRIVER("mute video plane.\n"); set_video_mute(DRM_MUTE_SET, mute); } int meson_plane_mute_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_meson_plane_mute *arg = data; if (!arg) { DRM_ERROR("para is NULL!\n"); return -EINVAL; } if (arg->plane_type == OSD_PLANE) { if (arg->plane_mute) { DRM_DEBUG_DRIVER("mute osd plane!\n"); osd_plane_mute(true); } else { DRM_DEBUG_DRIVER("unmute osd plane!\n"); osd_plane_mute(false); } return 0; } if (arg->plane_type == VIDEO_PLANE) { if (arg->plane_mute) { DRM_DEBUG_DRIVER("mute video plane!\n"); video_plane_mute(true); } else { DRM_DEBUG_DRIVER("unmute video plane!\n"); video_plane_mute(false); } return 0; } return -EINVAL; } #if IS_ENABLED(CONFIG_SYNC_FILE) int am_meson_dmabuf_export_sync_file_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_meson_dma_buf_export_sync_file *arg = data; struct dma_fence *fence = NULL; struct dma_buf *dmabuf = NULL; struct sync_file *sync_file; enum dma_resv_usage usage; int fd, ret = -1; MESON_DRM_FENCE("dmabuf-%px fence-%px", dmabuf, fence); if (arg->flags & ~DMA_BUF_SYNC_RW) return -EINVAL; if ((arg->flags & DMA_BUF_SYNC_RW) == 0) return -EINVAL; fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) return fd; dmabuf = dma_buf_get(arg->dmabuf_fd); if (IS_ERR_OR_NULL(dmabuf)) goto err_put_fd; if (arg->flags & DMA_BUF_SYNC_WRITE) { //fence = dma_resv_get_singleton_unlocked(dmabuf->resv); //if (IS_ERR(fence)) { // ret = PTR_ERR(fence); // goto err_put_fd; //} } else if (arg->flags & DMA_BUF_SYNC_READ) { usage = dma_resv_usage_rw(arg->flags & DMA_BUF_SYNC_WRITE); ret = dma_resv_get_singleton(dmabuf->resv, usage, &fence); if (ret) goto err_put_fd; } if (!fence) fence = dma_fence_get_stub(); sync_file = sync_file_create(fence); dma_fence_put(fence); dma_buf_put(dmabuf); if (!sync_file) { ret = -ENOMEM; goto err_put_fd; } fd_install(fd, sync_file->file); arg->fd = fd; MESON_DRM_FENCE("dmabuf-%px fence-%px fd-%d", dmabuf, fence, fd); return 0; err_put_fd: put_unused_fd(fd); MESON_DRM_FENCE("get fence fd failed! ret=%d\n", ret); return ret; } #endif struct meson_plane_supported_formats osd_formats = { .formats = supported_drm_formats, .format_num = ARRAY_SIZE(supported_drm_formats), }; struct meson_plane_supported_formats osd_formats_t5m = { .formats = supported_drm_formats_v2, .format_num = ARRAY_SIZE(supported_drm_formats_v2), }; struct meson_plane_supported_formats osd_formats_t6d = { .formats = supported_drm_formats_v6, .format_num = ARRAY_SIZE(supported_drm_formats_v6), }; struct meson_plane_supported_formats osd_formats_t3x = { .formats = supported_drm_formats_v3, .format_num = ARRAY_SIZE(supported_drm_formats_v3), }; struct meson_plane_supported_formats osd_formats_s1a = { .formats = supported_drm_formats_v4, .format_num = ARRAY_SIZE(supported_drm_formats_v4), }; struct meson_plane_supported_formats osd_formats_s7d = { .formats = supported_drm_formats_v5, .format_num = ARRAY_SIZE(supported_drm_formats_v5), }; struct meson_plane_supported_formats video_formats = { .formats = video_supported_drm_formats, .format_num = ARRAY_SIZE(video_supported_drm_formats), }; static void meson_plane_position_calc(struct meson_vpu_osd_layer_info *plane_info, struct drm_plane_state *state, struct meson_vpu_pipeline *pipeline) { u32 dst_w, dst_h, src_w, src_h, scan_mode_out; struct am_osd_plane *amp; struct drm_atomic_state *atomic_state = state->state; struct drm_crtc *crtc = state->crtc; struct drm_crtc_state *crtc_state; struct drm_display_mode *mode = NULL; if (crtc) { crtc_state = drm_atomic_get_crtc_state(atomic_state, crtc); if (crtc_state) mode = &crtc_state->mode; } else { DRM_DEBUG_DRIVER("Disabling plane %d, so skip position calc", plane_info->plane_index); return; } if (!crtc_state || !mode) mode = &pipeline->subs[crtc->index]->mode; scan_mode_out = mode->flags & DRM_MODE_FLAG_INTERLACE; plane_info->src_x = state->src_x >> 16; plane_info->src_y = state->src_y >> 16; plane_info->src_w = (state->src_w >> 16) & 0xffff; plane_info->src_h = (state->src_h >> 16) & 0xffff; plane_info->ori_src_x = plane_info->src_x; plane_info->ori_src_y = plane_info->src_y; plane_info->ori_src_w = plane_info->src_w; plane_info->ori_src_h = plane_info->src_h; plane_info->dst_x = state->crtc_x; plane_info->dst_y = state->crtc_y; plane_info->dst_w = state->crtc_w; plane_info->dst_h = state->crtc_h; plane_info->ori_dst_x = plane_info->dst_x; plane_info->ori_dst_y = plane_info->dst_y; plane_info->ori_dst_w = plane_info->dst_w; plane_info->ori_dst_h = plane_info->dst_h; plane_info->rotation = state->rotation; DRM_DEBUG_DRIVER("original:src[(%d %d),%dx%d]dst[(%d %d),%dx%d]mode[%dx%d]\n", plane_info->ori_src_x, plane_info->ori_src_y, plane_info->ori_src_w, plane_info->ori_src_h, plane_info->ori_dst_x, plane_info->ori_dst_y, plane_info->ori_dst_w, plane_info->ori_dst_h, mode->hdisplay, mode->vdisplay); if (state->plane) { amp = to_am_osd_plane(state->plane); if (amp->adjust_src.x1 > -1 && amp->adjust_src.y1 > -1 && amp->adjust_src.x2 > -1 && amp->adjust_src.y2 > -1) { plane_info->src_x = amp->adjust_src.x1; plane_info->src_y = amp->adjust_src.y1; plane_info->src_w = amp->adjust_src.x2; plane_info->src_h = amp->adjust_src.y2; DRM_DEBUG_DRIVER("resize original src [(%d %d), %dx%d]\n", amp->adjust_src.x1, amp->adjust_src.y1, amp->adjust_src.x2, amp->adjust_src.y2); } if (amp->adjust_dst.x1 > -1 && amp->adjust_dst.y1 > -1 && amp->adjust_dst.x2 > -1 && amp->adjust_dst.y2 > -1) { plane_info->dst_x = amp->adjust_dst.x1; plane_info->dst_y = amp->adjust_dst.y1; plane_info->dst_w = amp->adjust_dst.x2; plane_info->dst_h = amp->adjust_dst.y2; DRM_DEBUG_DRIVER("resize original dst [(%d %d), %dx%d]\n", amp->adjust_dst.x1, amp->adjust_dst.y1, amp->adjust_dst.x2, amp->adjust_dst.y2); } if (plane_info->rotation != amp->osd_reverse) plane_info->rotation = amp->osd_reverse; if (plane_info->blend_bypass != amp->osd_blend_bypass) plane_info->blend_bypass = amp->osd_blend_bypass; if (plane_info->read_ports != amp->osd_read_ports) plane_info->read_ports = amp->osd_read_ports; else plane_info->read_ports = 2; if (amp->zorder) plane_info->zorder = amp->zorder; } if (scan_mode_out) { plane_info->dst_y >>= 1; plane_info->dst_h >>= 1; } /*negative position process*/ if (plane_info->dst_x < 0) { dst_w = plane_info->dst_w + plane_info->dst_x; if (dst_w > 0) { src_w = plane_info->src_w * dst_w / plane_info->dst_w; plane_info->src_x += plane_info->src_w - src_w; plane_info->src_w = src_w; plane_info->dst_w = dst_w; plane_info->dst_x = 0; } else { plane_info->enable = 0; } DRM_DEBUG_DRIVER("state->crtc_x < 0\n"); } if (plane_info->dst_y < 0) { dst_h = plane_info->dst_h + plane_info->dst_y; if (dst_h > 0) { src_h = plane_info->src_h * dst_h / plane_info->dst_h; plane_info->src_y += plane_info->src_h - src_h; plane_info->src_h = src_h; plane_info->dst_h = dst_h; plane_info->dst_y = 0; } else { plane_info->enable = 0; } DRM_DEBUG_DRIVER("state->crtc_y < 0\n"); } /*overdisplay process*/ if ((plane_info->dst_x + plane_info->dst_w) > mode->hdisplay) { if (plane_info->dst_x >= mode->hdisplay) { plane_info->enable = 0; } else { dst_w = plane_info->dst_w; src_w = plane_info->src_w; plane_info->dst_w = mode->hdisplay - plane_info->dst_x; plane_info->src_w = (src_w * plane_info->dst_w) / dst_w; } DRM_DEBUG_DRIVER("(plane_info->dst_x + plane_info->dst_w) > mode->hdisplay\n"); } if ((plane_info->dst_y + plane_info->dst_h) > mode->vdisplay) { if (plane_info->dst_y >= mode->vdisplay) { plane_info->enable = 0; } else { dst_h = plane_info->dst_h; src_h = plane_info->src_h; plane_info->dst_h = mode->vdisplay - plane_info->dst_y; plane_info->src_h = (src_h * plane_info->dst_h) / dst_h; } DRM_DEBUG_DRIVER("(plane_info->dst_y + plane_info->dst_h) > mode->vdisplay\n"); } /*reverse process*/ if (plane_info->rotation & DRM_MODE_REFLECT_X) plane_info->dst_x = mode->hdisplay - plane_info->dst_w - plane_info->dst_x; if (plane_info->rotation & DRM_MODE_REFLECT_Y) plane_info->dst_y = mode->vdisplay - plane_info->dst_h - plane_info->dst_y; plane_info->hdisplay = mode->hdisplay; plane_info->vdisplay = mode->vdisplay; /* add for calculation of scaler coefficient */ plane_info->htotal = mode->htotal; plane_info->clock = mode->clock; DRM_DEBUG_DRIVER("modified:src[(%d %d),%dx%d]dst[(%d %d),%dx%d]enable[%d]\n", plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h, plane_info->enable); } static void meson_video_plane_position_calc(struct meson_vpu_video_layer_info *plane_info, struct drm_plane_state *state, struct meson_vpu_pipeline *pipeline) { u32 orig_w, orig_h; u32 crop_w = 0, crop_h = 0; struct drm_atomic_state *atomic_state = state->state; struct drm_crtc *crtc = state->crtc; struct drm_crtc_state *crtc_state; struct drm_display_mode *mode = NULL; struct am_video_plane *avp; if (crtc) { crtc_state = drm_atomic_get_crtc_state(atomic_state, crtc); if (crtc_state) mode = &crtc_state->mode; } else { DRM_DEBUG_DRIVER("Disabling video plane %d, so skip position calc", plane_info->plane_index); return; } if (!crtc_state || !mode) mode = &pipeline->subs[crtc->index]->mode; plane_info->src_x = state->src_x >> 16; plane_info->src_y = state->src_y >> 16; plane_info->src_w = (state->src_w >> 16) & 0xffff; plane_info->src_h = (state->src_h >> 16) & 0xffff; plane_info->ori_src_x = plane_info->src_x; plane_info->ori_src_y = plane_info->src_y; plane_info->ori_src_w = plane_info->src_w; plane_info->ori_src_h = plane_info->src_h; plane_info->dst_x = state->crtc_x; plane_info->dst_y = state->crtc_y; plane_info->dst_w = state->crtc_w; plane_info->dst_h = state->crtc_h; plane_info->ori_dst_x = plane_info->dst_x; plane_info->ori_dst_y = plane_info->dst_y; plane_info->ori_dst_w = plane_info->dst_w; plane_info->ori_dst_h = plane_info->dst_h; DRM_DEBUG_DRIVER("original:src[(%d %d),%dx%d]dst[(%d %d),%dx%d]mode[%dx%d]\n", plane_info->ori_src_x, plane_info->ori_src_y, plane_info->ori_src_w, plane_info->ori_src_h, plane_info->ori_dst_x, plane_info->ori_dst_y, plane_info->ori_dst_w, plane_info->ori_dst_h, mode->hdisplay, mode->vdisplay); if (state->plane) { avp = to_am_video_plane(state->plane); if (avp->adjust_src.x1 > -1 && avp->adjust_src.y1 > -1 && avp->adjust_src.x2 > -1 && avp->adjust_src.y2 > -1) { plane_info->src_x = avp->adjust_src.x1; plane_info->src_y = avp->adjust_src.y1; plane_info->src_w = avp->adjust_src.x2; plane_info->src_h = avp->adjust_src.y2; DRM_DEBUG_DRIVER("resize original src [(%d %d), %dx%d]\n", avp->adjust_src.x1, avp->adjust_src.y1, avp->adjust_src.x2, avp->adjust_src.y2); } if (avp->adjust_dst.x1 > -1 && avp->adjust_dst.y1 > -1 && avp->adjust_dst.x2 > -1 && avp->adjust_dst.y2 > -1) { plane_info->dst_x = avp->adjust_dst.x1; plane_info->dst_y = avp->adjust_dst.y1; plane_info->dst_w = avp->adjust_dst.x2; plane_info->dst_h = avp->adjust_dst.y2; DRM_DEBUG_DRIVER("resize original dst [(%d %d), %dx%d]\n", avp->adjust_dst.x1, avp->adjust_dst.y1, avp->adjust_dst.x2, avp->adjust_dst.y2); } } if (plane_info->is_uvm && plane_info->vf) { DRM_DEBUG_DRIVER("type=0x%x, video_axis_zoom=%d, comp[%u, %u][%u %u]\n", plane_info->vf->type, video_axis_zoom, plane_info->vf->compWidth, plane_info->vf->compHeight, plane_info->vf->width, plane_info->vf->height); if ((video_axis_zoom != -1) && (plane_info->vf->type & VIDTYPE_COMPRESS)) { plane_info->src_x = plane_info->src_x * (plane_info->vf->compWidth / plane_info->vf->width); plane_info->src_y = plane_info->src_y * (plane_info->vf->compHeight / plane_info->vf->height); plane_info->src_w = plane_info->src_w * (plane_info->vf->compWidth / plane_info->vf->width); plane_info->src_h = plane_info->src_h * (plane_info->vf->compHeight / plane_info->vf->height); DRM_DEBUG_DRIVER("COMPRESS:src[(%d %d), %dx%d]\n", plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h); } } orig_w = plane_info->src_w; orig_h = plane_info->src_h; if (!bypass_video_crop && !(plane_info->frame_flag & BIT(VIDEO_FRAME_BYPASS_CROP))) { /*negative position process*/ if (plane_info->dst_x < 0) { plane_info->src_x = (-plane_info->dst_x) * plane_info->src_w / plane_info->dst_w; plane_info->dst_w += state->crtc_x; DRM_DEBUG_DRIVER("x<0:src_x=%d, dst_w=%d\n", plane_info->src_x, plane_info->dst_w); if (plane_info->dst_w > 0) { plane_info->src_w -= plane_info->src_x; plane_info->dst_x = 0; } else { plane_info->enable = 0; } DRM_DEBUG_DRIVER("x<0:src_x=%d, src_w=%d, dst_x=%d, dst_w=%d, enable=%d\n", plane_info->src_x, plane_info->src_w, plane_info->dst_x, plane_info->dst_w, plane_info->enable); } if (plane_info->dst_y < 0) { plane_info->src_y = (-plane_info->dst_y) * plane_info->src_h / plane_info->dst_h; plane_info->dst_h += state->crtc_y; DRM_DEBUG_DRIVER("y<0:src_y=%d, dst_h=%d\n", plane_info->src_y, plane_info->dst_h); if (plane_info->dst_h > 0) { plane_info->src_h -= plane_info->src_y; plane_info->dst_y = 0; } else { plane_info->enable = 0; } DRM_DEBUG_DRIVER("y<0:src_y=%d, src_h=%d, dst_y=%d, dst_h=%d, enable=%d\n", plane_info->src_y, plane_info->src_h, plane_info->dst_y, plane_info->dst_h, plane_info->enable); } /*overdisplay process*/ if ((plane_info->dst_x + plane_info->dst_w) > mode->hdisplay) { if (plane_info->dst_x >= mode->hdisplay) { plane_info->enable = 0; } else { u32 crop_w_src; crop_w = plane_info->dst_x + plane_info->dst_w - mode->hdisplay; crop_w_src = crop_w * orig_w / state->crtc_w; if (plane_info->src_w >= crop_w_src) plane_info->src_w -= crop_w_src; else plane_info->src_w = 0; DRM_DEBUG_DRIVER("(x+w)>hdisplay:orig_w=%d, crop_w=%d, src_w=%d\n", orig_w, crop_w, plane_info->src_w); plane_info->dst_w -= crop_w; } DRM_DEBUG_DRIVER("(x+w)>hdisplay:src_w=%d, dst_w=%d, enable=%d\n", plane_info->src_w, plane_info->dst_w, plane_info->enable); } if ((plane_info->dst_y + plane_info->dst_h) > mode->vdisplay) { if (plane_info->dst_y >= mode->vdisplay) { plane_info->enable = 0; } else { u32 crop_h_src; crop_h = plane_info->dst_y + plane_info->dst_h - mode->vdisplay; crop_h_src = crop_h * orig_h / state->crtc_h; if (plane_info->src_h >= crop_h_src) plane_info->src_h -= crop_h_src; else plane_info->src_h = 0; DRM_DEBUG_DRIVER("(y+h)>vdisplay:orig_h=%d, crop_h=%d, src_h=%d\n", orig_h, crop_h, plane_info->src_h); plane_info->dst_h -= crop_h; } DRM_DEBUG_DRIVER("(y+h)>vdisplay:src_h=%d, dst_h=%d, enable=%d\n", plane_info->src_h, plane_info->dst_h, plane_info->enable); } } DRM_DEBUG_DRIVER("modified:src[(%d %d), %dx%d], dst[(%d %d), %dx%d]\n", plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h); DRM_DEBUG_DRIVER("modified:enable = %d, orig[%d %d], crop[%d %d]\n", plane_info->enable, orig_w, orig_h, crop_w, crop_h); } static int meson_plane_fb_check(struct drm_plane *plane, struct drm_plane_state *new_state, struct meson_vpu_osd_layer_info *plane_info) { struct drm_framebuffer *fb = new_state->fb; #ifdef CONFIG_AMLOGIC_DRM_USE_ION struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv = osd_plane->drv; struct am_meson_fb *meson_fb; #else struct drm_gem_cma_object *gem; #endif size_t fb_size = 0; phys_addr_t phyaddr; #ifdef CONFIG_AMLOGIC_DRM_USE_ION meson_fb = container_of(fb, struct am_meson_fb, base); if (!meson_fb) { DRM_DEBUG_DRIVER("meson_fb is NULL\n"); return -EINVAL; } DRM_DEBUG_DRIVER("meson_fb[id:%d,ref:%d]=0x%p\n", meson_fb->base.base.id, kref_read(&meson_fb->base.base.refcount), meson_fb); if (meson_fb->logo && meson_fb->logo->alloc_flag && meson_fb->logo->start) { phyaddr = meson_fb->logo->start; fb_size = meson_fb->logo->size; DRM_DEBUG_DRIVER("logo->phyaddr=0x%pa\n", &phyaddr); } else if (meson_fb->bufp[0]) { phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp[0], &fb_size); if (drv->logo && drv->logo->is_std) drv->logo->plane_has_fb = 1; } else { phyaddr = 0; DRM_INFO("don't find phyaddr!\n"); return -EINVAL; } #else if (!fb) { DRM_DEBUG_DRIVER("fb is NULL\n"); return -EINVAL; } /* Update Canvas with buffer address */ gem = drm_fb_cma_get_gem_obj(fb, 0); if (!gem) { DRM_INFO("gem is NULL!\n"); return -EINVAL; } phyaddr = gem->paddr; #endif plane_info->phy_addr = phyaddr; plane_info->fb_size = (u32)fb_size; return 0; } static struct vframe_s *get_vf_from_uvm(struct dma_buf *dmabuf) { struct vframe_s *vf = NULL; bool is_dec_vf = false; struct file_private_data *file_private_data = NULL; struct uvm_hook_mod *uhmod; if (!dmabuf) return NULL; is_dec_vf = is_valid_mod_type(dmabuf, VF_SRC_DECODER); if (is_dec_vf) { vf = dmabuf_get_vframe(dmabuf); if (vf) { DRM_DEBUG("vframe_type = 0x%x, vframe_flag = 0x%x.\n", vf->type, vf->flag); dmabuf_put_vframe(dmabuf); } else { DRM_DEBUG("vf is NULL.\n"); } } else { uhmod = uvm_get_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO); if (!uhmod) { DRM_DEBUG("uhmod is NULL\n"); return NULL; } if (IS_ERR_VALUE(uhmod) || !uhmod->arg) { DRM_DEBUG("file_private_data is NULL.\n"); return NULL; } file_private_data = uhmod->arg; uvm_put_hook_mod(dmabuf, VF_PROCESS_V4LVIDEO); vf = &file_private_data->vf; } return vf; } static int meson_video_plane_fb_check(struct drm_plane *plane, struct drm_plane_state *new_state, struct meson_vpu_video_layer_info *plane_info) { struct drm_framebuffer *fb = new_state->fb; #ifdef CONFIG_AMLOGIC_DRM_USE_ION struct am_video_plane *video_plane = to_am_video_plane(plane); struct meson_drm *drv = video_plane->drv; struct am_meson_fb *meson_fb; struct dma_buf *dmabuf; #else struct drm_gem_cma_object *gem; #endif size_t fb_size[2] = {0}; phys_addr_t phyaddr, phyaddr1 = 0; #ifdef CONFIG_AMLOGIC_DRM_USE_ION if (!fb) { DRM_INFO("fb is NULL!\n"); return -EINVAL; } meson_fb = container_of(fb, struct am_meson_fb, base); if (!meson_fb) { DRM_DEBUG_DRIVER("meson_fb is NULL!\n"); return -EINVAL; } DRM_DEBUG_DRIVER("meson_fb[id:%d,ref:%d]=0x%p\n", meson_fb->base.base.id, kref_read(&meson_fb->base.base.refcount), meson_fb); if (meson_fb->bufp[0]) { phyaddr = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp[0], &fb_size[0]); } else { //phyaddr = 0; DRM_INFO("don't find phyaddr!\n"); return -EINVAL; } if (meson_fb->bufp[1] && meson_fb->bufp[1] != meson_fb->bufp[0] && (fb->format->format == DRM_FORMAT_NV12 || fb->format->format == DRM_FORMAT_NV21 || fb->format->format == DRM_FORMAT_NV61 || fb->format->format == DRM_FORMAT_NV16)) { phyaddr1 = am_meson_gem_object_get_phyaddr(drv, meson_fb->bufp[1], &fb_size[1]); plane_info->dmabuf[1] = meson_fb->bufp[1]->base.dma_buf; if (!plane_info->dmabuf[1]) plane_info->dmabuf[1] = meson_fb->bufp[1]->dmabuf; } else { plane_info->dmabuf[1] = NULL; } /* start to get vframe from uvm */ if (meson_fb->bufp[0]->is_uvm) { dmabuf = meson_fb->bufp[0]->base.dma_buf; plane_info->vf = get_vf_from_uvm(dmabuf); plane_info->is_uvm = meson_fb->bufp[0]->is_uvm; plane_info->dmabuf[0] = dmabuf; } else { plane_info->vf = NULL; plane_info->is_uvm = 0; plane_info->dmabuf[0] = meson_fb->bufp[0]->base.dma_buf; if (!plane_info->dmabuf[0]) plane_info->dmabuf[0] = meson_fb->bufp[0]->dmabuf; } if (!plane_info->dmabuf[0]) { DRM_ERROR("plane_info->dmabuf[0] is NULL!\n"); return -EINVAL; } DRM_DEBUG_DRIVER("dmabuf %px, %px, is_uvm %d, vf %px\n", plane_info->dmabuf[0], plane_info->dmabuf[1], plane_info->is_uvm, plane_info->vf); #else if (!fb) { DRM_DEBUG_DRIVER("fb is NULL!\n"); return -EINVAL; } /* Update Canvas with buffer address */ gem = drm_fb_cma_get_gem_obj(fb, 0); if (!gem) { DRM_INFO("gem is NULL!\n"); return -EINVAL; } phyaddr = gem->paddr; #endif plane_info->phy_addr[0] = phyaddr; plane_info->fb_size[0] = (u32)fb_size[0]; plane_info->phy_addr[1] = phyaddr1; plane_info->fb_size[1] = (u32)fb_size[1]; return 0; } static int meson_plane_get_fb_info(struct drm_plane *plane, struct drm_plane_state *new_state, struct meson_vpu_osd_layer_info *plane_info) { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct drm_framebuffer *fb = new_state->fb; struct meson_drm *drv = osd_plane->drv; if (!drv) { DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__); return -EINVAL; } if (osd_plane->plane_index >= MESON_MAX_OSDS) { DRM_INFO("%s invalid plane_index!\n", __func__); return -EINVAL; } plane_info->pixel_format = fb->format->format; plane_info->byte_stride = fb->pitches[0]; plane_info->fb_w = fb->width; plane_info->fb_h = fb->height; plane_info->afbc_en = 0; plane_info->afbc_inter_format = 0; plane_info->process_unit = MIF_MODE; plane_info->afrc_cu_bits = 0; if (fb->modifier) { //afrc if ((fb->modifier >> 52 & 0xf) == DRM_FORMAT_MOD_ARM_TYPE_AFRC) { if ((fb->modifier & AFRC_FORMAT_MOD_CU_SIZE_MASK) == AFRC_FORMAT_MOD_CU_SIZE_16) plane_info->afrc_cu_bits = 0; else if ((fb->modifier & AFRC_FORMAT_MOD_CU_SIZE_MASK) == AFRC_FORMAT_MOD_CU_SIZE_24) plane_info->afrc_cu_bits = 1; else if ((fb->modifier & AFRC_FORMAT_MOD_CU_SIZE_MASK) == AFRC_FORMAT_MOD_CU_SIZE_32) plane_info->afrc_cu_bits = 2; plane_info->process_unit = GFCD_AFRC; } else { if (drv->vpu_data && drv->vpu_data->has_gfcd && (drv->of_conf.gfcd_enable || plane_info->pixel_format == DRM_FORMAT_ABGR10101010)) { plane_info->process_unit = GFCD_AFBC; if (am_drm_param.force_mali_afbc_mode) { plane_info->process_unit = MALI_AFBC; plane_info->afbc_en = 1; plane_info->afbc_inter_format = AFBC_EN; } } else { plane_info->afbc_en = 1; plane_info->afbc_inter_format = AFBC_EN; } if (fb->modifier & AFBC_FORMAT_MOD_YTR) plane_info->afbc_inter_format |= YUV_TRANSFORM; if (fb->modifier & AFBC_FORMAT_MOD_SPLIT) plane_info->afbc_inter_format |= BLOCK_SPLIT; if (fb->modifier & AFBC_FORMAT_MOD_TILED) plane_info->afbc_inter_format |= TILED_HEADER_EN; if ((fb->modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) == AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) plane_info->afbc_inter_format |= SUPER_BLOCK_ASPECT; } } if (!drv->of_conf.gfcd_enable && (plane_info->process_unit == GFCD_AFBC || plane_info->process_unit == GFCD_AFRC)) DRM_DEBUG_DRIVER("gfcd enable config mismatched with real frame,please check dts!"); DRM_DEBUG_DRIVER("flags=%d,pixel_format=0x%x,modifier=0x%llx,stride=%d\n", fb->flags, fb->format->format, fb->modifier, plane_info->byte_stride); DRM_DEBUG_DRIVER("afbc_en=%u,afbc_inter_format=0x%x,process_unit=%d,afrc_cu_bits=%d\n", plane_info->afbc_en, plane_info->afbc_inter_format, plane_info->process_unit, plane_info->afrc_cu_bits); return 0; } static int meson_video_plane_get_fb_info(struct drm_plane *plane, struct drm_plane_state *new_state, struct meson_vpu_video_layer_info *plane_info) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct drm_framebuffer *fb = new_state->fb; struct meson_drm *drv = video_plane->drv; if (!drv) { DRM_INFO("%s new_state/meson_drm is NULL!\n", __func__); return -EINVAL; } if (video_plane->plane_index >= MESON_MAX_VIDEO) { DRM_INFO("%s invalid plane_index!\n", __func__); return -EINVAL; } plane_info->pixel_format = fb->format->format; plane_info->byte_stride = fb->pitches[0]; plane_info->fb_w = fb->width; plane_info->fb_h = fb->height; DRM_DEBUG_DRIVER("flags=%d,modifier=%llu,fb=%dx%d,stride=%d,pixel_format=0x%x\n", fb->flags, fb->modifier, plane_info->fb_w, plane_info->fb_h, plane_info->byte_stride, plane_info->pixel_format); return 0; } static bool meson_video_plane_is_repeat_frame(struct meson_vpu_video *mvv) { struct meson_vpu_block *mvb = &mvv->base; struct meson_vpu_block_state *mvbs = priv_to_block_state(mvb->obj.state); struct meson_vpu_video_state *mvvs = to_video_state(mvbs); if (mvvs->repeat_frame) MESON_DRM_FENCE("video repeat frame!\n"); return mvvs->repeat_frame; } static const char *am_meson_video_fence_get_driver_name(struct dma_fence *fence) { return "aml_drm"; } static void am_meson_video_fence_release(struct dma_fence *fence) { kfree_rcu(fence, rcu); fence = NULL; } static const char * am_meson_video_fence_get_timeline_name(struct dma_fence *fence) { return "video_fence"; } static const char * am_meson_video_present_fence_get_timeline_name(struct dma_fence *fence) { return "video_present_fence"; } static const struct dma_fence_ops am_meson_video_plane_fence_ops = { .get_driver_name = am_meson_video_fence_get_driver_name, .get_timeline_name = am_meson_video_fence_get_timeline_name, .release = am_meson_video_fence_release, }; static const struct dma_fence_ops am_meson_video_plane_present_fence_ops = { .get_driver_name = am_meson_video_fence_get_driver_name, .get_timeline_name = am_meson_video_present_fence_get_timeline_name, .release = am_meson_video_fence_release, }; static struct dma_fence *am_meson_video_create_fence(spinlock_t *lock, struct am_video_plane *video_plane) { struct dma_fence *fence; fence = kzalloc(sizeof(*fence), GFP_KERNEL); if (!fence) return NULL; dma_fence_init(fence, &am_meson_video_plane_fence_ops, lock, video_plane->context, ++video_plane->seqno); return fence; } static struct dma_fence *am_meson_video_create_present_fence(spinlock_t *lock, struct am_video_plane *video_plane) { struct dma_fence *fence; fence = kzalloc(sizeof(*fence), GFP_KERNEL); if (!fence) return NULL; dma_fence_init(fence, &am_meson_video_plane_present_fence_ops, lock, video_plane->present_context, ++video_plane->present_seqno); return fence; } static void set_release_fence_for_video(struct am_meson_video_plane_state *plane_state, s32 __user *fence_ptr) { plane_state->video_release_fence_state.video_release_ptr = fence_ptr; MESON_DRM_FENCE("%px, %px\n", plane_state, fence_ptr); } static s32 __user *get_release_fence_for_video(struct am_meson_video_plane_state *plane_state) { s32 __user *fence_ptr; fence_ptr = plane_state->video_release_fence_state.video_release_ptr; plane_state->video_release_fence_state.video_release_ptr = NULL; MESON_DRM_FENCE("%px, %px\n", plane_state, fence_ptr); return fence_ptr; } static int meson_video_prepare_fence(struct drm_plane *plane, struct drm_plane_state *state, struct meson_vpu_video *mvv) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct dma_fence *fence = NULL; struct drm_video_release_fence_state *fence_state; struct am_meson_video_plane_state *plane_state; int ret = 0; s32 __user *fence_ptr; plane_state = to_am_meson_video_plane_state(state); fence_ptr = get_release_fence_for_video(plane_state); fence_state = &plane_state->video_release_fence_state; /*creat implicit fence as out_fence, and next will directly *export to video composer module */ fence = am_meson_video_create_fence(&video_plane->fence_lock, video_plane); if (!fence) return -ENOMEM; mvv->fence = fence; if (fence_ptr) { fence_state->fd = get_unused_fd_flags(O_CLOEXEC); if (fence_state->fd < 0) { ret = fence_state->fd; goto err_put_fence; } if (put_user(fence_state->fd, fence_ptr)) { ret = -EFAULT; MESON_DRM_FENCE("put_user fail\n"); goto err_put_fence; } fence_state->sync_file = sync_file_create(fence); if (!fence_state->sync_file) { ret = -ENOMEM; goto err_put_fd; } fd_install(fence_state->fd, fence_state->sync_file->file); MESON_DRM_FENCE("creat release fence(%px) seq:(%llx,%llx),plane_index%d\n", fence, video_plane->context, video_plane->seqno, video_plane->plane_index); return 0; err_put_fd: put_unused_fd(fence_state->fd); err_put_fence: dma_fence_put(fence); } MESON_DRM_FENCE("creat fence(%px) seq:(%llx,%llx),plane_index%d\n", fence, video_plane->context, video_plane->seqno, video_plane->plane_index); return ret; } static int meson_video_prepare_repeat_fence(struct drm_plane *plane, struct drm_plane_state *state, struct meson_vpu_video *mvv) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct drm_video_release_fence_state *fence_state; struct am_meson_video_plane_state *plane_state; int ret = 0; s32 __user *fence_ptr; if (!mvv->fence) { WARN_ON(!mvv->fence); return -EFAULT; } plane_state = to_am_meson_video_plane_state(state); fence_ptr = get_release_fence_for_video(plane_state); fence_state = &plane_state->video_release_fence_state; if (fence_ptr) { fence_state->fd = get_unused_fd_flags(O_CLOEXEC); if (fence_state->fd < 0) { ret = fence_state->fd; goto err_put_fence; } if (put_user(fence_state->fd, fence_ptr)) { ret = -EFAULT; MESON_DRM_FENCE("put_user fail\n"); goto err_put_fence; } fence_state->sync_file = sync_file_create(mvv->fence); if (!fence_state->sync_file) { ret = -ENOMEM; goto err_put_fd; } fd_install(fence_state->fd, fence_state->sync_file->file); MESON_DRM_FENCE("repeat fence(%px) seq:(%llx,%llx),plane_index%d\n", mvv->fence, video_plane->context, video_plane->seqno, video_plane->plane_index); return 0; err_put_fd: put_unused_fd(fence_state->fd); err_put_fence: dma_fence_put(mvv->fence); } return ret; } static void set_present_fence_for_video(struct am_meson_video_plane_state *plane_state, s32 __user *fence_ptr) { plane_state->video_present_fence_state.video_present_ptr = fence_ptr; MESON_DRM_FENCE("%px, %px\n", plane_state, fence_ptr); } static s32 __user *get_present_fence_for_video(struct am_meson_video_plane_state *plane_state) { s32 __user *fence_ptr; fence_ptr = plane_state->video_present_fence_state.video_present_ptr; plane_state->video_present_fence_state.video_present_ptr = NULL; MESON_DRM_FENCE("%px, %px\n", plane_state, fence_ptr); return fence_ptr; } static int meson_video_prepare_present_fence(struct drm_plane *plane, struct drm_plane_state *state, struct meson_vpu_video *mvv) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct dma_fence *fence = NULL; struct drm_video_present_fence_state *fence_state; struct am_meson_video_plane_state *plane_state; int ret = 0; s32 __user *fence_ptr; plane_state = to_am_meson_video_plane_state(state); fence_ptr = get_present_fence_for_video(plane_state); fence_state = &plane_state->video_present_fence_state; /*creat implicit fence as out_fence, and next will directly *export to video composer module */ if (fence_ptr) { fence = am_meson_video_create_present_fence(&video_plane->present_fence_lock, video_plane); if (!fence) return -ENOMEM; fence_state->fd = get_unused_fd_flags(O_CLOEXEC); if (fence_state->fd < 0) { ret = fence_state->fd; goto err_put_fence; } if (put_user(fence_state->fd, fence_ptr)) { ret = -EFAULT; MESON_DRM_FENCE("put_user fail\n"); goto err_put_fence; } fence_state->sync_file = sync_file_create(fence); if (!fence_state->sync_file) { ret = -ENOMEM; goto err_put_fd; } fd_install(fence_state->fd, fence_state->sync_file->file); mvv->present_fence = fence; MESON_DRM_FENCE("fence(%px), fd(%d), plane_index%d\n", fence, fence_state->fd, video_plane->plane_index); return 0; err_put_fd: put_unused_fd(fence_state->fd); err_put_fence: dma_fence_put(fence); } return ret; } static int meson_plane_atomic_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val) { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct am_meson_plane_state *plane_state; struct meson_drm *drv = osd_plane->drv; int ret = 0; plane_state = to_am_meson_plane_state(state); if (property == osd_plane->occupied_property) { *val = osd_plane->osd_occupied; return 0; } else if (property == osd_plane->prop_sec_en) { *val = plane_state->sec_en; ret = 0; } else if (property == osd_plane->palette) { *val = osd_plane->palette_id; ret = 0; } else if (property == osd_plane->rotation_reflect_property) { *val = osd_plane->osd_reverse; ret = 0; } else if (property == osd_plane->unsupport_nonafbc) { if (!drv) { DRM_INFO("%s meson_drm is NULL!\n", __func__); return -EINVAL; } *val = drv->pipeline->osd_axi_sel; ret = 0; } else if (property == osd_plane->dimm_ctrl_property) { *val = osd_plane->osd_dimm.dimm_value; ret = 0; } else if (property == osd_plane->osd_capability_property) { *val = drv->pipeline->osd_capability[osd_plane->plane_index]; ret = 0; } return ret; } static int meson_plane_atomic_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val) { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct am_meson_plane_state *plane_state; struct drm_property_blob *new_blob = NULL; int ret = 0; plane_state = to_am_meson_plane_state(state); if (property == osd_plane->occupied_property) { osd_plane->osd_occupied = val; return 0; } else if (property == osd_plane->prop_sec_en) { plane_state->sec_en = val; ret = 0; } else if (property == osd_plane->palette) { osd_plane->palette_id = val; new_blob = drm_property_lookup_blob(osd_plane->drv->drm, val); if (!new_blob) return ret; osd_plane->receive_palette = new_blob->data; drm_property_blob_put(new_blob); ret = 0; } else if (property == osd_plane->rotation_reflect_property) { osd_plane->osd_reverse = val; ret = 0; } else if (property == osd_plane->dimm_ctrl_property) { osd_plane->osd_dimm.dimm_value = val; osd_plane->osd_dimm.dimm_ctrl = 1; ret = 0; } return ret; } static int meson_video_plane_atomic_get_property(struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct am_meson_video_plane_state *plane_state; struct meson_vpu_pipeline *ppl = video_plane->pipeline; struct meson_vpu_video *mvv = ppl->video[video_plane->plane_index]; plane_state = to_am_meson_video_plane_state(state); if (property == video_plane->signal_fmt_property) { *val = plane_state->signal_fmt; } else if (property == video_plane->frame_flag_prop) { *val = 0; } else if (property == video_plane->video_cap_property) { *val = mvv->video_cap; } else if (property == video_plane->video_src_min_size_property) { *val = mvv->src_min_size; } else if (property == video_plane->video_src_max_size_property) { *val = mvv->src_max_size; } else if (property == video_plane->async_in_fence_property) { *val = -1; } else if (property == video_plane->video_present_fence_property) { *val = 0; } else if (property == video_plane->video_release_fence_property) { *val = 0; } else { return -EINVAL; } return 0; } static int meson_video_plane_atomic_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct am_meson_video_plane_state *plane_state; plane_state = to_am_meson_video_plane_state(state); if (property == video_plane->signal_fmt_property) { plane_state->signal_fmt = val; return 0; } else if (property == video_plane->frame_flag_prop) { plane_state->frame_flag = val; return 0; } else if (property == video_plane->async_in_fence_property) { if (plane_state->async_in_fence) return -EINVAL; if (U642I64(val) == -1) return 0; plane_state->async_in_fence = sync_file_get_fence(val); if (!plane_state->async_in_fence) return -EINVAL; } else if (property == video_plane->video_present_fence_property) { s32 __user *fence_ptr = u64_to_user_ptr(val); if (!fence_ptr) return 0; if (put_user(-1, fence_ptr)) return -EFAULT; set_present_fence_for_video(plane_state, fence_ptr); } else if (property == video_plane->video_release_fence_property) { s32 __user *fence_ptr = u64_to_user_ptr(val); if (!fence_ptr) return 0; if (put_user(-1, fence_ptr)) return -EFAULT; set_release_fence_for_video(plane_state, fence_ptr); } return 0; } int meson_async_atomic_plane_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_file *file_priv, struct drm_property *property, u64 val) { struct am_video_plane *video_plane = NULL; struct am_meson_video_plane_state *plane_state = NULL; struct drm_device *dev = plane->dev; struct drm_mode_config *config = &dev->mode_config; bool replaced = false; int ret; if (plane->funcs == &am_video_plane_funs) { video_plane = to_am_video_plane(plane); plane_state = to_am_meson_video_plane_state(state); } if (property == config->prop_fb_id) { struct drm_framebuffer *fb; fb = drm_framebuffer_lookup(dev, file_priv, val); drm_atomic_set_fb_for_plane(state, fb); if (fb) drm_framebuffer_put(fb); } else if (property == config->prop_in_fence_fd) { if (state->fence) return -EINVAL; if (U642I64(val) == -1) return 0; state->fence = sync_file_get_fence(val); if (!state->fence) return -EINVAL; } else if (video_plane && property == video_plane->async_in_fence_property) { if (plane_state->async_in_fence) return -EINVAL; if (U642I64(val) == -1) return 0; plane_state->async_in_fence = sync_file_get_fence(val); MESON_DRM_FENCE("plane_state->async_in_fence %px", plane_state->async_in_fence); if (!plane_state->async_in_fence) return -EINVAL; } else if (video_plane && property == video_plane->video_present_fence_property) { s32 __user *fence_ptr = u64_to_user_ptr(val); if (!fence_ptr) return 0; if (put_user(-1, fence_ptr)) return -EFAULT; set_present_fence_for_video(plane_state, fence_ptr); } else if (video_plane && property == video_plane->video_release_fence_property) { s32 __user *fence_ptr = u64_to_user_ptr(val); if (!fence_ptr) return 0; if (put_user(-1, fence_ptr)) return -EFAULT; set_release_fence_for_video(plane_state, fence_ptr); } else if (property == config->prop_crtc_id) { struct drm_crtc *crtc = drm_crtc_find(dev, file_priv, val); if (val && !crtc) return -EACCES; return drm_atomic_set_crtc_for_plane(state, crtc); } else if (property == config->prop_crtc_x) { state->crtc_x = U642I64(val); } else if (property == config->prop_crtc_y) { state->crtc_y = U642I64(val); } else if (property == config->prop_crtc_w) { state->crtc_w = val; } else if (property == config->prop_crtc_h) { state->crtc_h = val; } else if (property == config->prop_src_x) { state->src_x = val; } else if (property == config->prop_src_y) { state->src_y = val; } else if (property == config->prop_src_w) { state->src_w = val; } else if (property == config->prop_src_h) { state->src_h = val; } else if (property == plane->alpha_property) { state->alpha = val; } else if (property == plane->blend_mode_property) { state->pixel_blend_mode = val; } else if (property == plane->rotation_property) { if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] bad rotation bitmask: 0x%llx\n", plane->base.id, plane->name, val); return -EINVAL; } state->rotation = val; } else if (property == plane->zpos_property) { state->zpos = val; } else if (property == plane->color_encoding_property) { state->color_encoding = val; } else if (property == plane->color_range_property) { state->color_range = val; } else if (property == config->prop_fb_damage_clips) { ret = drm_property_replace_blob_from_id(dev, &state->fb_damage_clips, val, -1, sizeof(struct drm_rect), &replaced); return ret; } else if (property == plane->scaling_filter_property) { state->scaling_filter = val; } else if (plane->funcs->atomic_set_property) { return plane->funcs->atomic_set_property(plane, state, property, val); } else { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n", plane->base.id, plane->name, property->base.id, property->name); return -EINVAL; } return 0; } static struct drm_plane_state * meson_plane_duplicate_state(struct drm_plane *plane) { struct am_meson_plane_state *meson_plane_state; if (WARN_ON(!plane->state)) return NULL; DRM_DEBUG_DRIVER("(%s)\n", plane->name); meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL); if (!meson_plane_state) return NULL; __drm_atomic_helper_plane_duplicate_state(plane, &meson_plane_state->base); return &meson_plane_state->base; } static struct drm_plane_state * meson_video_plane_duplicate_state(struct drm_plane *plane) { struct am_meson_video_plane_state *meson_plane_state; if (WARN_ON(!plane->state)) return NULL; DRM_DEBUG("(%s)\n", plane->name); meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL); if (!meson_plane_state) return NULL; __drm_atomic_helper_plane_duplicate_state(plane, &meson_plane_state->base); return &meson_plane_state->base; } static void meson_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) { struct am_meson_plane_state *meson_plane_state; meson_plane_state = to_am_meson_plane_state(state); __drm_atomic_helper_plane_destroy_state(&meson_plane_state->base); kfree(meson_plane_state); } static void meson_video_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) { struct am_meson_video_plane_state *meson_plane_state; meson_plane_state = to_am_meson_video_plane_state(state); __drm_atomic_helper_plane_destroy_state(&meson_plane_state->base); kfree(meson_plane_state); } static void meson_osd_plane_reset(struct drm_plane *plane) { struct am_meson_plane_state *meson_plane_state; int zpos = 0; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); zpos = osd_plane->plane_index + OSD_PLANE_BEGIN_ZORDER; if (plane->state) { meson_plane_destroy_state(plane, plane->state); plane->state = NULL; } meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL); if (!meson_plane_state) return; __drm_atomic_helper_plane_reset(plane, &meson_plane_state->base); meson_plane_state->base.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE; /*reset zpos property unless it set by hwc*/ if (meson_plane_state->base.zpos == 0) meson_plane_state->base.zpos = zpos; } static void meson_video_plane_reset(struct drm_plane *plane) { struct am_meson_video_plane_state *meson_plane_state; int zpos = 0; struct am_video_plane *video_plane = to_am_video_plane(plane); zpos = video_plane->plane_index + VIDEO_PLANE_BEGIN_ZORDER; if (plane->state) { meson_plane_state = to_am_meson_video_plane_state(plane->state); __drm_atomic_helper_plane_destroy_state(plane->state); kfree(meson_plane_state); plane->state = NULL; } meson_plane_state = kzalloc(sizeof(*meson_plane_state), GFP_KERNEL); if (!meson_plane_state) return; __drm_atomic_helper_plane_reset(plane, &meson_plane_state->base); /*reset zpos property unless it set by hwc*/ if (meson_plane_state->base.zpos == 0) meson_plane_state->base.zpos = zpos; } bool am_meson_vpu_check_format_mod(struct drm_plane *plane, u32 format, u64 modifier) { DRM_DEBUG_DRIVER("modifier %llu", modifier); if (modifier == DRM_FORMAT_MOD_INVALID) return false; if (modifier == DRM_FORMAT_MOD_LINEAR) return true; switch (format) { case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: /* YTR is forbidden for non XBGR formats */ if (modifier & AFBC_FORMAT_MOD_YTR) return false; fallthrough; case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_ABGR10101010: case DRM_FORMAT_RGBA8888: return true; case DRM_FORMAT_RGB888: /* YTR is forbidden for non XBGR formats */ //if (modifier & AFBC_FORMAT_MOD_YTR) // return false; return true; case DRM_FORMAT_RGB565: /* YTR is forbidden for non XBGR formats */ if (modifier & AFBC_FORMAT_MOD_YTR) return false; return true; /* TOFIX support mode formats */ default: DRM_DEBUG_DRIVER("unsupported afbc format[%08x]\n", format); return false; } } bool am_meson_vpu_check_video_format_mod(struct drm_plane *plane, u32 format, u64 modifier) { if (modifier == DRM_FORMAT_MOD_LINEAR && format != DRM_FORMAT_YUV420_8BIT && format != DRM_FORMAT_YUV420_10BIT) return true; if ((modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) == DRM_FORMAT_MOD_AMLOGIC_FBC(0, 0)) { unsigned int layout = modifier & DRM_FORMAT_MOD_AMLOGIC_FBC(__fourcc_mod_amlogic_layout_mask, 0); unsigned int options = (modifier >> __fourcc_mod_amlogic_options_shift) & __fourcc_mod_amlogic_options_mask; if (format != DRM_FORMAT_YUV420_8BIT && format != DRM_FORMAT_YUV420_10BIT) { DRM_DEBUG_DRIVER("%llx invalid format 0x%08x\n", modifier, format); return false; } if (layout != AMLOGIC_FBC_LAYOUT_BASIC && layout != AMLOGIC_FBC_LAYOUT_SCATTER) { DRM_DEBUG_DRIVER("%llx invalid layout %x\n", modifier, layout); return false; } if (options && options != AMLOGIC_FBC_OPTION_MEM_SAVING) { DRM_DEBUG_DRIVER("%llx invalid layout %x\n", modifier, layout); return false; } return true; } DRM_DEBUG_DRIVER("invalid modifier %llx for format 0x%08x\n", modifier, format); return false; } static void meson_osd_plane_atomic_print_state(struct drm_printer *p, const struct drm_plane_state *state) { struct drm_plane *plane; struct meson_vpu_osd_layer_info *plane_info; struct meson_vpu_sub_pipeline_state *mvps; struct am_osd_plane *osd_plane; struct meson_drm *drv; struct drm_private_state *obj_state; u32 crtc_index = 0; if (!state) { DRM_INFO("%s state/meson_drm is NULL!\n", __func__); return; } plane = state->plane; if (!plane) { DRM_INFO("%s drm_plane is NULL!\n", __func__); return; } osd_plane = to_am_osd_plane(plane); DRM_DEBUG_DRIVER("[%d]\n", osd_plane->plane_index); drv = osd_plane->drv; if (!drv || !drv->pipeline) { DRM_INFO("%s private state or pipeline is NULL!\n", __func__); return; } if (state->crtc) crtc_index = state->crtc->index; else if (drv->pipeline->osd_crtc_index[osd_plane->plane_index] != -1) crtc_index = drv->pipeline->osd_crtc_index[osd_plane->plane_index]; obj_state = drv->pipeline->subs[crtc_index]->obj.state; if (!obj_state) { DRM_ERROR("null pipeline obj state!\n"); return; } mvps = container_of(obj_state, struct meson_vpu_sub_pipeline_state, obj); if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) { DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__); return; } plane_info = &mvps->plane_info[osd_plane->plane_index]; drm_printf(p, "\tmeson osd plane %d info:\n", osd_plane->plane_index); drm_printf(p, "\t\tori_src_x=%u\n", plane_info->ori_src_x); drm_printf(p, "\t\tori_src_y=%u\n", plane_info->ori_src_y); drm_printf(p, "\t\tori_src_w=%u\n", plane_info->ori_src_w); drm_printf(p, "\t\tori_src_h=%u\n", plane_info->ori_src_h); drm_printf(p, "\t\tori_dst_x=%d\n", plane_info->ori_dst_x); drm_printf(p, "\t\tori_dst_y=%d\n", plane_info->ori_dst_y); drm_printf(p, "\t\tori_dst_w=%u\n", plane_info->ori_dst_w); drm_printf(p, "\t\tori_dst_h=%u\n", plane_info->ori_dst_h); drm_printf(p, "\t\tsrc_x=%u\n", plane_info->src_x); drm_printf(p, "\t\tsrc_y=%u\n", plane_info->src_y); drm_printf(p, "\t\tsrc_w=%u\n", plane_info->src_w); drm_printf(p, "\t\tsrc_h=%u\n", plane_info->src_h); drm_printf(p, "\t\tdst_x=%d\n", plane_info->dst_x); drm_printf(p, "\t\tdst_y=%d\n", plane_info->dst_y); drm_printf(p, "\t\tdst_w=%u\n", plane_info->dst_w); drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_h); drm_printf(p, "\t\tsrc_w_offset4hdr=%d\n", plane_info->src_w_offset4hdr); drm_printf(p, "\t\tfb_w=%u\n", plane_info->fb_w); drm_printf(p, "\t\tfb_h=%u\n", plane_info->fb_h); drm_printf(p, "\t\tzorder=%u\n", plane_info->zorder); drm_printf(p, "\t\tbyte_stride=%u\n", plane_info->byte_stride); drm_printf(p, "\t\tpixel_format=%u\n", plane_info->pixel_format); drm_printf(p, "\t\tphy_addr=0x%llx\n", plane_info->phy_addr); drm_printf(p, "\t\tplane_index=%u\n", plane_info->plane_index); drm_printf(p, "\t\tuhd_plane_index=%u\n", plane_info->uhd_plane_index); drm_printf(p, "\t\tenable=%u\n", plane_info->enable); drm_printf(p, "\t\tratio_x=%u\n", plane_info->ratio_x); drm_printf(p, "\t\tafbc_inter_format=%u\n", plane_info->afbc_inter_format); drm_printf(p, "\t\tafbc_en=%u\n", plane_info->afbc_en); drm_printf(p, "\t\tprocess_unit=%u\n", plane_info->process_unit); drm_printf(p, "\t\tfb_size=%u\n", plane_info->fb_size); drm_printf(p, "\t\tpixel_blend=%u\n", plane_info->pixel_blend); drm_printf(p, "\t\trotation=%u\n", plane_info->rotation); drm_printf(p, "\t\tblend_bypass=%u\n", plane_info->blend_bypass); drm_printf(p, "\t\tglobal_alpha=%u\n", plane_info->global_alpha); drm_printf(p, "\t\tscaling_filter=%u\n", plane_info->scaling_filter); drm_printf(p, "\t\tread_ports=%u\n", plane_info->read_ports); drm_printf(p, "\t\tdisable_thread_en=%u\n", drv->of_conf.disable_thread_en); } static void meson_video_plane_atomic_print_state(struct drm_printer *p, const struct drm_plane_state *state) { struct drm_plane *plane; struct meson_vpu_video_layer_info *plane_info; struct meson_video_sub_pipeline_state *mvps; struct am_video_plane *video_plane; struct meson_drm *drv; struct drm_private_state *obj_state; u32 crtc_index = 0; if (!state) { DRM_INFO("%s state/meson_drm is NULL!\n", __func__); return; } plane = state->plane; if (!plane) { DRM_INFO("%s drm_plane is NULL!\n", __func__); return; } video_plane = to_am_video_plane(plane); DRM_DEBUG_DRIVER("[%d]\n", video_plane->plane_index); drv = video_plane->drv; if (!drv || !drv->pipeline) { DRM_INFO("%s private state is NULL!\n", __func__); return; } if (state->crtc) crtc_index = state->crtc->index; else if (drv->pipeline->video_crtc_index[video_plane->plane_index] != -1) crtc_index = drv->pipeline->video_crtc_index[video_plane->plane_index]; obj_state = drv->pipeline->video_subs[crtc_index].obj.state; if (!obj_state) { DRM_ERROR("null pipeline obj state!\n"); return; } mvps = container_of(obj_state, struct meson_video_sub_pipeline_state, obj); if (!mvps || video_plane->plane_index >= MESON_MAX_VIDEO) { DRM_INFO("%s mvps/video_plane is NULL!\n", __func__); return; } plane_info = &mvps->video_plane_info[video_plane->plane_index]; drm_printf(p, "\tmeson video plane %d info:\n", video_plane->plane_index); drm_printf(p, "\t\tori_src_x=%u\n", plane_info->ori_src_x); drm_printf(p, "\t\tori_src_y=%u\n", plane_info->ori_src_y); drm_printf(p, "\t\tori_src_w=%u\n", plane_info->ori_src_w); drm_printf(p, "\t\tori_src_h=%u\n", plane_info->ori_src_h); drm_printf(p, "\t\tori_dst_x=%d\n", plane_info->ori_dst_x); drm_printf(p, "\t\tori_dst_y=%d\n", plane_info->ori_dst_y); drm_printf(p, "\t\tori_dst_w=%u\n", plane_info->ori_dst_w); drm_printf(p, "\t\tori_dst_h=%u\n", plane_info->ori_dst_h); drm_printf(p, "\t\tsrc_x=%u\n", plane_info->src_x); drm_printf(p, "\t\tsrc_y=%u\n", plane_info->src_y); drm_printf(p, "\t\tsrc_w=%u\n", plane_info->src_w); drm_printf(p, "\t\tsrc_h=%u\n", plane_info->src_h); drm_printf(p, "\t\tdst_x=%d\n", plane_info->dst_x); drm_printf(p, "\t\tdst_y=%d\n", plane_info->dst_y); drm_printf(p, "\t\tdst_w=%u\n", plane_info->dst_w); drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_h); drm_printf(p, "\t\tzorder=%u\n", plane_info->zorder); drm_printf(p, "\t\tbyte_stride=%u\n", plane_info->byte_stride); drm_printf(p, "\t\tpixel_format=%u\n", plane_info->pixel_format); drm_printf(p, "\t\tphy_addr[0]=0x%llx\n", plane_info->phy_addr[0]); drm_printf(p, "\t\tphy_addr[1]=0x%llx\n", plane_info->phy_addr[1]); drm_printf(p, "\t\tplane_index=%u\n", plane_info->plane_index); drm_printf(p, "\t\tenable=%u\n", plane_info->enable); drm_printf(p, "\t\tratio_x=%u\n", plane_info->ratio_x); drm_printf(p, "\t\tfb_size[0]=%u\n", plane_info->fb_size[0]); drm_printf(p, "\t\tfb_size[1]=%u\n", plane_info->fb_size[1]); drm_printf(p, "\t\tpixel_blend=%u\n", plane_info->pixel_blend); /*TODO: vframe_s */ drm_printf(p, "\t\tis_uvm=%u\n", plane_info->is_uvm); } static const struct drm_plane_funcs am_osd_plane_funs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, .reset = meson_osd_plane_reset, .atomic_duplicate_state = meson_plane_duplicate_state, .atomic_destroy_state = meson_plane_destroy_state, .atomic_set_property = meson_plane_atomic_set_property, .atomic_get_property = meson_plane_atomic_get_property, .format_mod_supported = am_meson_vpu_check_format_mod, .atomic_print_state = meson_osd_plane_atomic_print_state, }; static const struct drm_plane_funcs am_video_plane_funs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, .reset = meson_video_plane_reset, .atomic_duplicate_state = meson_video_plane_duplicate_state, .atomic_destroy_state = meson_video_plane_destroy_state, .atomic_set_property = meson_video_plane_atomic_set_property, .atomic_get_property = meson_video_plane_atomic_get_property, .format_mod_supported = am_meson_vpu_check_video_format_mod, .atomic_print_state = meson_video_plane_atomic_print_state, }; static int meson_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { return 0; } static void meson_plane_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state) { //struct am_osd_plane *osd_plane = to_am_osd_plane(plane); //DRM_DEBUG("osd %d.\n", osd_plane->plane_index); } static void meson_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { struct meson_vpu_osd_layer_info *plane_info; struct meson_vpu_sub_pipeline_state *mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct drm_crtc *crtc = NULL; struct meson_drm *drv; struct drm_private_state *obj_state; int crtc_index = 0; if (!plane->state) { DRM_INFO("%s state is NULL!\n", __func__); return; } drv = osd_plane->drv; if (!drv || !drv->pipeline) { DRM_INFO("%s private state or pipeline is NULL!\n", __func__); return; } crtc = plane->state->crtc; if (crtc) crtc_index = crtc->index; obj_state = drv->pipeline->subs[crtc_index]->obj.state; if (!obj_state) { DRM_ERROR("null pipeline obj state!\n"); return; } mvps = container_of(obj_state, struct meson_vpu_sub_pipeline_state, obj); if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) { DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__); return; } plane_info = &mvps->plane_info[osd_plane->plane_index]; if (mvps->more_60) { osd_enable[OSD1_SLICE0] = 1; osd_enable[OSD3_SLICE1] = 1; DRM_DEBUG_DRIVER("mode_60hz case, osd0_status=%d osd2_status=%d\n", osd_enable[OSD1_SLICE0], osd_enable[OSD3_SLICE1]); return; } if (plane_info->enable == 1) osd_enable[plane_info->plane_index] = 1; else osd_enable[plane_info->plane_index] = 0; DRM_DEBUG_DRIVER("plane_index=%d, osd_status=%d\n", plane_info->plane_index, osd_enable[plane_info->plane_index]); } static void meson_video_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *old_atomic_state) { int video_index, crtc_index; struct meson_video_sub_pipeline *mvsp; struct am_video_plane *video_plane = to_am_video_plane(plane); struct drm_crtc *crtc = plane->state->crtc; struct am_meson_crtc *amcrtc = to_am_meson_crtc(crtc); struct meson_vpu_pipeline *pipeline = amcrtc->priv->pipeline; struct drm_plane_state *new_plane_state; struct meson_vpu_video *mvv = pipeline->video[video_plane->plane_index]; crtc_index = crtc->index; video_index = video_plane->plane_index; mvsp = &pipeline->video_subs[crtc_index]; new_plane_state = drm_atomic_get_new_plane_state(old_atomic_state, plane); DRM_DEBUG_DRIVER("video plane atomic_update new_plane_state %p\n", new_plane_state); if (new_plane_state && !meson_video_plane_is_repeat_frame(mvv)) { meson_video_prepare_fence(plane, new_plane_state, mvv); meson_video_prepare_present_fence(plane, new_plane_state, mvv); } if (new_plane_state && meson_video_plane_is_repeat_frame(mvv)) meson_video_prepare_repeat_fence(plane, new_plane_state, mvv); video_pipeline_block_update(mvsp, old_atomic_state, video_index); } #if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION) ||\ defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM) /* -1: invalid osd index * 0: osd is disabled * 1: osd is enabled */ int get_osd_status_callback(u32 index) { if (index < MESON_MAX_OSDS) return osd_enable[index]; else return -1; } #endif static int meson_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *atomic_state) { struct meson_vpu_osd_layer_info *plane_info; struct meson_vpu_osd_layer_info *old_plane_info = NULL; struct meson_vpu_sub_pipeline_state *mvps, *old_mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv = osd_plane->drv; struct drm_plane_state *state; struct am_meson_plane_state *plane_state; u16 blend_mask_val; u32 crtc_index = 0; int ret; DRM_DEBUG_DRIVER("IN, pid[%s %d], plane_index[%d]\n", current->comm, current->pid, osd_plane->plane_index); state = drm_atomic_get_new_plane_state(atomic_state, plane); if (!state || !drv) { DRM_INFO("%s state/meson_drm is NULL!\n", __func__); return -EINVAL; } if (state->crtc) crtc_index = state->crtc->index; else if (drv->pipeline->osd_crtc_index[osd_plane->plane_index] != -1) crtc_index = drv->pipeline->osd_crtc_index[osd_plane->plane_index]; mvps = meson_vpu_pipeline_get_state(drv->pipeline->subs[crtc_index], atomic_state); if (PTR_ERR(mvps) == -EDEADLK) return -EDEADLK; if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) { DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__); return -EINVAL; } plane_info = &mvps->plane_info[osd_plane->plane_index]; plane_info->plane_index = osd_plane->plane_index; plane_info->osd_dimm.dimm_value = osd_plane->osd_dimm.dimm_value; plane_info->osd_dimm.dimm_ctrl = osd_plane->osd_dimm.dimm_ctrl; /*get plane prop value*/ plane_info->zorder = state->zpos; blend_mask_val = osd_plane->pixel_blend_debug; if ((blend_mask_val & 0xf0) == 0x10) /* forced pixel blend */ state->pixel_blend_mode = blend_mask_val & 0x0f; plane_info->pixel_blend = state->pixel_blend_mode; plane_info->global_alpha = state->alpha; plane_info->scaling_filter = (u32)state->scaling_filter; if (osd_plane->receive_palette) plane_info->palette_arry = osd_plane->receive_palette; mvps->plane_index[osd_plane->plane_index] = osd_plane->plane_index; meson_plane_position_calc(plane_info, state, mvps->pipeline); old_mvps = meson_vpu_pipeline_get_old_state(drv->pipeline->subs[crtc_index], state->state); if (old_mvps) { old_plane_info = &old_mvps->plane_info[osd_plane->plane_index]; plane_info->status_changed = plane_info->enable != old_plane_info->enable; } else { DRM_DEBUG_DRIVER("old_mvps is NULL\n"); plane_info->status_changed = 1; } ret = meson_plane_fb_check(plane, state, plane_info); if (ret < 0) { plane_info->enable = 0; DRM_DEBUG_DRIVER("plane%d fb is NULL,disable the plane!\n", plane_info->plane_index); return 0; } ret = meson_plane_get_fb_info(plane, state, plane_info); if (ret < 0 || plane_info->src_w > MESON_OSD_INPUT_W_LIMIT || plane_info->src_w == 0) { plane_info->enable = 0; DRM_DEBUG_DRIVER("fb is invalid, disable plane, src_w=%d, ret=%d\n", plane_info->src_w, ret); return ret; } if (old_plane_info) { if (plane_info->pixel_format != old_plane_info->pixel_format) { DRM_DEBUG_DRIVER("pixel format changed [0x%x -> 0x%x].\n", old_plane_info->pixel_format, plane_info->pixel_format); } } plane_info->enable = 1; if (osd_plane->osd_permanent_blank) { plane_info->enable = 0; DRM_INFO("osd-%d is forcibly disabled by debug node.\n", osd_plane->plane_index); } plane_info->crtc_index = crtc_index; drv->pipeline->osd_crtc_index[osd_plane->plane_index] = crtc_index; plane_state = to_am_meson_plane_state(state); plane_info->sec_en = plane_state->sec_en; MESON_DRM_STATE("osd%d (\"%s\" pid=%d]) state(0x%p)\n" "mvps:0x%p phy:0x%llx src[%d %d %d %d] dst[%d %d %d %d]\n", plane_info->plane_index, current->comm, task_pid_nr(current), atomic_state, mvps, plane_info->phy_addr, plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h); DRM_DEBUG_DRIVER("OSD PLANE index=%d, zorder=%d, premult= %d, alpha = %d, phy = %llx\n", plane_info->plane_index, plane_info->zorder, state->pixel_blend_mode, plane_info->global_alpha, plane_info->phy_addr); DRM_DEBUG_DRIVER("w/h = %d/%d, src_x/y/w/h=%d/%d/%d/%d, dimm=[0x%x,%d]\n", plane_info->fb_w, plane_info->fb_h, plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h, plane_info->osd_dimm.dimm_value, plane_info->osd_dimm.dimm_ctrl); DRM_DEBUG_DRIVER("status_changed = %d, dst_x/y/w/h=%d/%d/%d/%d\n", plane_info->status_changed, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h); return 0; } static int meson_video_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *atomic_state) { struct meson_video_sub_pipeline *mvsp; struct meson_vpu_video_layer_info *plane_info, *old_plane_info; struct meson_video_sub_pipeline_state *mvps, *old_mvps; struct am_video_plane *video_plane = to_am_video_plane(plane); struct meson_drm *drv = video_plane->drv; struct drm_plane_state *state; struct am_meson_video_plane_state *plane_state; int ret, crtc_index; state = drm_atomic_get_new_plane_state(atomic_state, plane); if (!state || !drv) { DRM_INFO("%s state/meson_drm is NULL!\n", __func__); return -EINVAL; } if (state->crtc) crtc_index = state->crtc->index; else if (drv->pipeline->video_crtc_index[video_plane->plane_index] != -1) crtc_index = drv->pipeline->video_crtc_index[video_plane->plane_index]; else crtc_index = 0; mvsp = &drv->pipeline->video_subs[crtc_index]; mvps = meson_video_pipeline_get_state(mvsp, atomic_state); if (PTR_ERR(mvps) == -EDEADLK) { DRM_DEBUG_DRIVER("mvps is error!\n"); return -EDEADLK; } if (!mvps || video_plane->plane_index >= MESON_MAX_VIDEO) { DRM_INFO("%s mvps/video_plane is NULL!\n", __func__); return -EINVAL; } DRM_DEBUG_DRIVER("IN, pid[%s %d], plane_index[%d], crtc_index[%d]\n", current->comm, current->pid, video_plane->plane_index, crtc_index); plane_info = &mvps->video_plane_info[video_plane->plane_index]; plane_info->plane_index = video_plane->plane_index; plane_info->zorder = state->zpos; plane_info->rotation = state->rotation; plane_state = to_am_meson_video_plane_state(state); plane_info->signal_fmt = plane_state->signal_fmt; plane_info->frame_flag = plane_state->frame_flag; plane_info->in_fence = plane_state->async_in_fence; mvps->video_plane_index[video_plane->plane_index] = video_plane->plane_index; ret = meson_video_plane_fb_check(plane, state, plane_info); if (ret < 0) { plane_info->enable = 0; plane_info->dmabuf[0] = NULL; plane_info->dmabuf[1] = NULL; video_pipeline_block_check(mvsp, atomic_state, video_plane->plane_index); DRM_DEBUG_DRIVER("plane%d fb is NULL,disable the plane!\n", plane_info->plane_index); return 0; } meson_video_plane_position_calc(plane_info, state, drv->pipeline); old_mvps = meson_video_pipeline_get_old_state(mvsp, atomic_state); if (old_mvps) { old_plane_info = &old_mvps->video_plane_info[video_plane->plane_index]; if (plane_info->src_w != old_plane_info->src_w || plane_info->src_h != old_plane_info->src_h || plane_info->dst_x != old_plane_info->dst_x || plane_info->dst_y != old_plane_info->dst_y || plane_info->dst_w != old_plane_info->dst_w || plane_info->dst_h != old_plane_info->dst_h || plane_info->zorder != old_plane_info->zorder || !plane_info->enable) { DRM_DEBUG_DRIVER("old:src(%dx%d)dst(%d,%d)(%dx%d), zorder=%d\n", old_plane_info->src_w, old_plane_info->src_h, old_plane_info->dst_x, old_plane_info->dst_y, old_plane_info->dst_w, old_plane_info->dst_h, old_plane_info->zorder); DRM_DEBUG_DRIVER("new:src(%dx%d)dst(%d,%d)(%dx%d), zorder=%d, enable=%d\n", plane_info->src_w, plane_info->src_h, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h, plane_info->zorder, plane_info->enable); plane_info->status_changed = 1; } else { plane_info->status_changed = 0; } } else { DRM_DEBUG_DRIVER("old_mvps is NULL\n"); plane_info->status_changed = 1; } ret = meson_video_plane_get_fb_info(plane, state, plane_info); if (ret < 0 || plane_info->src_w == 0) { DRM_DEBUG_DRIVER("fb is invalid, disable plane, src_w=%d, ret=%d\n", plane_info->src_w, ret); plane_info->enable = 0; video_pipeline_block_check(mvsp, atomic_state, video_plane->plane_index); return ret; } plane_info->crtc_index = crtc_index; drv->pipeline->video_crtc_index[video_plane->plane_index] = crtc_index; plane_info->enable = 1; video_pipeline_block_check(mvsp, atomic_state, video_plane->plane_index); MESON_DRM_STATE("video%d (\"%s\" pid=%d]) state:0x%p\n" "mvps:0x%p dmabuf:0x%px src[%d %d %d %d] dst[%d %d %d %d]\n", plane_info->plane_index, current->comm, task_pid_nr(current), atomic_state, mvps, plane_info->dmabuf[0], plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h); DRM_DEBUG_DRIVER("VIDEO PLANE index=%d, zorder=%d\n", plane_info->plane_index, plane_info->zorder); DRM_DEBUG_DRIVER("src_x/y/w/h=%d/%d/%d/%d\n", plane_info->src_x, plane_info->src_y, plane_info->src_w, plane_info->src_h); DRM_DEBUG_DRIVER("status_changed = %d, dst_x/y/w/h=%d/%d/%d/%d\n", plane_info->status_changed, plane_info->dst_x, plane_info->dst_y, plane_info->dst_w, plane_info->dst_h); return 0; } static void meson_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *old_state) { struct meson_vpu_osd_layer_info *plane_info; struct meson_vpu_sub_pipeline_state *mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv; struct drm_private_state *obj_state; struct drm_plane_state *old_plane_state; if (!plane->state) { DRM_INFO("%s state is NULL!\n", __func__); return; } old_plane_state = drm_atomic_get_old_plane_state(old_state, plane); if (!old_plane_state) { DRM_INFO("%s old plane state is NULL!\n", __func__); return; } drv = osd_plane->drv; if (!drv || !drv->pipeline) { DRM_INFO("%s private state or pipeline is NULL!\n", __func__); return; } obj_state = drv->pipeline->subs[old_plane_state->crtc->index]->obj.state; if (!obj_state) { DRM_ERROR("null pipeline obj state!\n"); return; } mvps = container_of(obj_state, struct meson_vpu_sub_pipeline_state, obj); if (!mvps || osd_plane->plane_index >= MESON_MAX_OSDS) { DRM_INFO("%s mvps/osd_plane is NULL!\n", __func__); return; } plane_info = &mvps->plane_info[osd_plane->plane_index]; osd_enable[plane_info->plane_index] = 0; if (mvps->more_60 && plane_info->plane_index == OSD1_SLICE0) { osd_enable[OSD1_SLICE0] = 0; osd_enable[OSD3_SLICE1] = 0; DRM_DEBUG_DRIVER("mode_60hz case, osd0_status=%d osd2_status=%d\n", osd_enable[OSD1_SLICE0], osd_enable[OSD3_SLICE1]); } DRM_DEBUG_DRIVER("plane_index=%d, osd_status=%d\n", plane_info->plane_index, osd_enable[plane_info->plane_index]); } static void meson_video_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *old_atomic_state) { int video_index, crtc_index; struct meson_video_sub_pipeline *sub_pipe; struct am_video_plane *video_plane = to_am_video_plane(plane); struct meson_vpu_pipeline *pipeline = video_plane->pipeline; unsigned long pos_crtcs = plane->possible_crtcs; crtc_index = find_first_bit(&pos_crtcs, 32); if (crtc_index >= MESON_MAX_CRTC) crtc_index = 0; video_index = video_plane->plane_index; sub_pipe = &pipeline->video_subs[crtc_index]; DRM_DEBUG_DRIVER("video %d, crtc %d.\n", video_index, crtc_index); video_pipeline_block_update(sub_pipe, old_atomic_state, video_index); } /*add async check & atomic funs*/ int meson_osd_plane_async_check(struct drm_plane *plane, struct drm_atomic_state *atomic_state) { int ret = 0; struct meson_vpu_sub_pipeline_state *mvps; struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct meson_drm *drv = osd_plane->drv; struct meson_vpu_osd_layer_info *plane_info; struct drm_plane_state *new_state; DRM_DEBUG_DRIVER("plane_index-%d\n", osd_plane->plane_index); new_state = drm_atomic_get_new_plane_state(atomic_state, plane); if (!new_state) { DRM_ERROR("atomic state is NULL!\n"); return -EINVAL; } mvps = meson_vpu_pipeline_get_state(drv->pipeline->subs[new_state->crtc->index], atomic_state); if (PTR_ERR(mvps) == -EDEADLK) return -EDEADLK; if (!mvps) { DRM_ERROR("mvps is NULL.\n"); return -EINVAL; } plane_info = &mvps->plane_info[osd_plane->plane_index]; if ((plane_info->enable && !new_state->fb) || !plane_info->enable) { DRM_ERROR("osd plane visible state changed, enable=%d,fb=%p\n", plane_info->enable, new_state->fb); return -EINVAL; } if (plane_info->status_changed) { DRM_DEBUG("osd%d plane info changed\n", osd_plane->plane_index); return -EINVAL; } return ret; } int meson_video_plane_async_check(struct drm_plane *plane, struct drm_atomic_state *atomic_state) { int crtc_index; struct meson_vpu_video_layer_info *plane_info; struct meson_video_sub_pipeline *mvsp; struct meson_video_sub_pipeline_state *mvps; struct drm_plane_state *new_state; struct am_video_plane *video_plane = to_am_video_plane(plane); struct meson_drm *drv = video_plane->drv; DRM_DEBUG_DRIVER("plane_index-%d\n", video_plane->plane_index); new_state = drm_atomic_get_new_plane_state(atomic_state, plane); if (!new_state) { DRM_ERROR("atomic state is NULL!\n"); return -EINVAL; } if (new_state->crtc) crtc_index = new_state->crtc->index; else crtc_index = 0; mvsp = &drv->pipeline->video_subs[crtc_index]; mvps = meson_video_pipeline_get_state(mvsp, atomic_state); if (PTR_ERR(mvps) == -EDEADLK) return -EDEADLK; if (!mvps) { DRM_ERROR("mvps is NULL.\n"); return -EINVAL; } plane_info = &mvps->video_plane_info[video_plane->plane_index]; if ((plane_info->enable && !new_state->fb) || !plane_info->enable) { DRM_ERROR("vide plane visible state changed, enable=%d,fb=%p\n", plane_info->enable, new_state->fb); return -EINVAL; } return 0; } void meson_osd_plane_async_update(struct drm_plane *plane, struct drm_atomic_state *state) { struct am_osd_plane *osd_plane = to_am_osd_plane(plane); struct drm_plane_state *old_plane_state; struct drm_plane_state *new_state; struct meson_vpu_sub_pipeline *sub_pipe; struct am_meson_crtc *amcrtc; struct meson_vpu_pipeline *pipeline; struct drm_private_obj *obj; struct meson_vpu_sub_pipeline_state *cur_sps, *new_sps; int i, crtc_index; struct drm_private_state *new_obj_state; old_plane_state = drm_atomic_get_old_plane_state(state, plane); if (!old_plane_state) { DRM_ERROR("plane state is null.\n"); return; } new_state = drm_atomic_get_new_plane_state(state, plane); if (!new_state || !new_state->state) { DRM_ERROR("atomic state is NULL![%p]\n", new_state); return; } amcrtc = to_am_meson_crtc(new_state->crtc); pipeline = amcrtc->pipeline; crtc_index = amcrtc->crtc_index; sub_pipe = pipeline->subs[crtc_index]; DRM_DEBUG_DRIVER("plane_index-%d, crtc_index=%d, fb=%p, src=%dx%d, dst=%dx%d\n", osd_plane->plane_index, crtc_index, new_state->fb, new_state->src_x, new_state->src_y, new_state->crtc_x, new_state->crtc_y); do { struct drm_connector *connector; struct drm_connector_state *old_conn_state; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct drm_plane *plane; struct drm_plane_state *old_plane_state; struct drm_crtc_commit *commit; int ret; for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { commit = old_crtc_state->commit; if (!commit) continue; ret = wait_for_completion_interruptible(&commit->hw_done); if (ret) DRM_ERROR("crtc timeout!"); } for_each_old_connector_in_state(state, connector, old_conn_state, i) { commit = old_conn_state->commit; if (!commit) continue; ret = wait_for_completion_interruptible(&commit->hw_done); if (ret) DRM_ERROR("connector timeout!"); } for_each_old_plane_in_state(state, plane, old_plane_state, i) { commit = old_plane_state->commit; if (!commit) continue; ret = wait_for_completion_interruptible(&commit->hw_done); if (ret) DRM_ERROR("plane timeout!"); } } while (0); meson_commit_reenter_inc(pipeline->priv, crtc_index, ASYNC_MODE); plane->state->src_x = new_state->src_x; plane->state->src_y = new_state->src_y; plane->state->src_w = new_state->src_w; plane->state->src_h = new_state->src_h; plane->state->crtc_x = new_state->crtc_x; plane->state->crtc_y = new_state->crtc_y; plane->state->crtc_w = new_state->crtc_w; plane->state->crtc_h = new_state->crtc_h; plane->state->zpos = new_state->zpos; swap(plane->state->fb, new_state->fb); for_each_new_private_obj_in_state(state, obj, new_obj_state, i) { if (obj != &sub_pipe->obj) continue; new_sps = priv_to_sub_pipeline_state(new_obj_state); cur_sps = priv_to_sub_pipeline_state(obj->state); cur_sps->plane_info[osd_plane->plane_index] = new_sps->plane_info[osd_plane->plane_index]; } /* * C5 does not have a scanout pos register, so there is * no need to determine when a register is written. */ if (amcrtc->pipeline->priv->vpu_data->enc_method != ENC_METHOD_NULL) vpu_pipeline_prepare_update(pipeline, new_state->crtc->mode.vdisplay, drm_mode_vrefresh(&new_state->crtc->mode), crtc_index); if (vpu_pipeline_osd_update(sub_pipe, state)) goto out; vpu_pipeline_finish_update(pipeline, crtc_index); out: meson_commit_reenter_dec(pipeline->priv, crtc_index, ASYNC_MODE); } void meson_video_plane_async_update(struct drm_plane *plane, struct drm_atomic_state *state) { struct am_video_plane *video_plane = to_am_video_plane(plane); struct meson_video_sub_pipeline *sub_pipe; struct am_meson_crtc *amcrtc = NULL; struct meson_vpu_pipeline *pipeline = video_plane->pipeline; struct meson_vpu_video *mvv; int crtc_index = 0; struct drm_plane_state *old_plane_state; struct drm_plane_state *new_state; int i; struct drm_private_obj *obj; struct drm_private_state *old_obj_state, *new_obj_state; unsigned long flags; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; old_plane_state = drm_atomic_get_old_plane_state(state, plane); if (!old_plane_state) { DRM_ERROR("plane state is null.\n"); return; } new_state = drm_atomic_get_new_plane_state(state, plane); if (!new_state || !new_state->state) { DRM_ERROR("atomic state is NULL![%p %p]\n", new_state, new_state->state); return; } DRM_DEBUG_DRIVER("plane_index-%d, crtc-%p\n", video_plane->plane_index, new_state->crtc); if (new_state->crtc) { amcrtc = to_am_meson_crtc(new_state->crtc); crtc_index = amcrtc->crtc_index; } sub_pipe = &pipeline->video_subs[crtc_index]; mvv = pipeline->video[video_plane->plane_index]; DRM_DEBUG_DRIVER("plane_index=%d,crtc=%p,crtc_index=%d,fb=%p,src=%dx%d,dst=%dx%d\n", video_plane->plane_index, new_state->crtc, crtc_index, new_state->fb, new_state->src_x, new_state->src_y, new_state->crtc_x, new_state->crtc_y); plane->state->src_x = new_state->src_x; plane->state->src_y = new_state->src_y; plane->state->src_w = new_state->src_w; plane->state->src_h = new_state->src_h; plane->state->crtc_x = new_state->crtc_x; plane->state->crtc_y = new_state->crtc_y; plane->state->crtc_w = new_state->crtc_w; plane->state->crtc_h = new_state->crtc_h; plane->state->zpos = new_state->zpos; swap(plane->state->fb, new_state->fb); for_each_oldnew_private_obj_in_state(state, obj, old_obj_state, new_obj_state, i) { if (obj != &sub_pipe->obj && obj != &mvv->base.obj) continue; old_obj_state->state = state; new_obj_state->state = NULL; state->private_objs[i].state = old_obj_state; obj->state = new_obj_state; } if (new_state && !meson_video_plane_is_repeat_frame(mvv)) { meson_video_prepare_fence(plane, new_state, mvv); meson_video_prepare_present_fence(plane, new_state, mvv); } if (new_state && meson_video_plane_is_repeat_frame(mvv)) meson_video_prepare_repeat_fence(plane, new_state, mvv); video_pipeline_block_update(sub_pipe, new_state->state, video_plane->plane_index); for_each_new_crtc_in_state(state, crtc, crtc_state, i) { spin_lock_irqsave(&crtc->dev->event_lock, flags); DRM_DEBUG_VBL("add event to list, event=%p, crct=%d!\n", crtc_state->event, drm_crtc_vblank_get(crtc)); if (crtc_state->event) { if (drm_crtc_vblank_get(crtc) == 0) drm_crtc_arm_vblank_event(crtc, crtc_state->event); else if (amcrtc) amcrtc->event = crtc_state->event; crtc_state->event = NULL; } spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } } static const struct drm_plane_helper_funcs am_osd_helper_funcs = { .prepare_fb = meson_plane_prepare_fb, .cleanup_fb = meson_plane_cleanup_fb, .atomic_update = meson_plane_atomic_update, .atomic_check = meson_plane_atomic_check, .atomic_disable = meson_plane_atomic_disable, .atomic_async_check = meson_osd_plane_async_check, .atomic_async_update = meson_osd_plane_async_update, }; static const struct drm_plane_helper_funcs am_video_helper_funcs = { .prepare_fb = meson_plane_prepare_fb, .cleanup_fb = meson_plane_cleanup_fb, .atomic_update = meson_video_plane_atomic_update, .atomic_check = meson_video_plane_atomic_check, .atomic_disable = meson_video_plane_atomic_disable, .atomic_async_check = meson_video_plane_async_check, .atomic_async_update = meson_video_plane_async_update, }; static void meson_plane_create_security_en_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create_bool(drm_dev, 0, "SEC_EN"); if (prop) { osd_plane->prop_sec_en = prop; drm_object_attach_property(&osd_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to SEC_EN property\n"); } } struct drm_property * meson_create_scaling_filter_prop(struct drm_device *dev, unsigned int supported_filters) { struct drm_property *prop; static const struct drm_prop_enum_list props[] = { { DRM_SCALING_FILTER_DEFAULT, "Default" }, { DRM_SCALING_FILTER_NEAREST_NEIGHBOR, "Nearest Neighbor" }, { DRM_SCALING_FILTER_BICUBIC_SHARP, "Bicubic_Sharp" }, { DRM_SCALING_FILTER_BICUBIC, "Bicubic" }, { DRM_SCALING_FILTER_BILINEAR, "Bilinear" }, { DRM_SCALING_FILTER_2POINT_BILINEAR, "2Point_Bilinear" }, { DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP, "3Point_Triangle_Sharp" }, { DRM_SCALING_FILTER_3POINT_TRIANGLE, "3Point_Triangle" }, { DRM_SCALING_FILTER_4POINT_TRIANGLE, "4Point_Triangle" }, { DRM_SCALING_FILTER_4POINT_BSPLINE, "4Point_BSPline" }, { DRM_SCALING_FILTER_3POINT_BSPLINE, "3Point_BSPline" }, { DRM_SCALING_FILTER_REPEATE, "Repeate" }, }; unsigned int valid_mode_mask = BIT(DRM_SCALING_FILTER_DEFAULT) | BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR) | BIT(DRM_SCALING_FILTER_BICUBIC_SHARP) | BIT(DRM_SCALING_FILTER_BICUBIC) | BIT(DRM_SCALING_FILTER_BILINEAR) | BIT(DRM_SCALING_FILTER_2POINT_BILINEAR) | BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP) | BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE) | BIT(DRM_SCALING_FILTER_4POINT_TRIANGLE) | BIT(DRM_SCALING_FILTER_4POINT_BSPLINE) | BIT(DRM_SCALING_FILTER_3POINT_BSPLINE) | BIT(DRM_SCALING_FILTER_REPEATE); int i; if (WARN_ON((supported_filters & ~valid_mode_mask) || ((supported_filters & BIT(DRM_SCALING_FILTER_DEFAULT)) == 0))) return ERR_PTR(-EINVAL); prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, "SCALING_FILTER", hweight32(supported_filters)); if (!prop) return ERR_PTR(-ENOMEM); for (i = 0; i < ARRAY_SIZE(props); i++) { int ret; if (!(BIT(props[i].type) & supported_filters)) continue; ret = drm_property_add_enum(prop, props[i].type, props[i].name); if (ret) { drm_property_destroy(dev, prop); return ERR_PTR(ret); } } return prop; } int meson_plane_create_scaling_filter_property(struct drm_plane *plane, unsigned int supported_filters) { struct drm_property *prop = meson_create_scaling_filter_prop(plane->dev, supported_filters); if (IS_ERR(prop)) return PTR_ERR(prop); drm_object_attach_property(&plane->base, prop, DRM_SCALING_FILTER_DEFAULT); plane->scaling_filter_property = prop; return 0; } static void meson_plane_add_occupied_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create_bool(drm_dev, 0, "meson.plane.occupied"); if (prop) { osd_plane->occupied_property = prop; drm_object_attach_property(&osd_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to occupied property\n"); } } static void meson_plane_add_palette_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB, "palette", 1); if (prop) { osd_plane->palette = prop; drm_object_attach_property(&osd_plane->base.base, prop, 12); } else { DRM_ERROR("Failed to palette property\n"); } } static void meson_plane_add_unsupport_nonafbc_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create_bool(drm_dev, 0, "unsupport_nonafbc"); if (prop) { osd_plane->unsupport_nonafbc = prop; drm_object_attach_property(&osd_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to add unsupport_nonafbc property\n"); } } static const u32 meson_plane_fb_size_list[] = { 1080 << 16 | 1920, 2160 << 16 | 3840, }; static void meson_plane_add_max_fb_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { int ret; u32 size_index = 0, max_fb_size; struct drm_property *prop; ret = of_property_read_u32(drm_dev->dev->of_node, "max_fb_size", &size_index); if (ret) max_fb_size = meson_plane_fb_size_list[0]; else max_fb_size = meson_plane_fb_size_list[size_index]; prop = drm_property_create_range(drm_dev, DRM_MODE_PROP_IMMUTABLE, "max_fb_size", 0, UINT_MAX); if (prop) { osd_plane->max_fb_property = prop; drm_object_attach_property(&osd_plane->base.base, prop, max_fb_size); } else { DRM_ERROR("Failed to create max_fb property\n"); } } static void meson_plane_add_osd_capability_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "osd_capability", 0, UINT_MAX); if (prop) { osd_plane->osd_capability_property = prop; drm_object_attach_property(&osd_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create osd_capability property\n"); } } static const struct drm_prop_enum_list osd_rotation_reflect_list[] = { { DRM_MODE_ROTATE_0, "reflect-0" }, { DRM_MODE_REFLECT_X, "reflect-x" }, { DRM_MODE_REFLECT_Y, "reflect-y" }, { DRM_MODE_REFLECT_MASK, "reflect-all" }, }; static void meson_plane_add_rotation_reflect_property(struct drm_device *drm_dev, struct am_osd_plane *osd_plane) { struct drm_property *prop; prop = drm_property_create_enum(drm_dev, 0, "rotation_reflect", osd_rotation_reflect_list, ARRAY_SIZE(osd_rotation_reflect_list)); if (prop) { osd_plane->rotation_reflect_property = prop; drm_object_attach_property(&osd_plane->base.base, prop, DRM_MODE_ROTATE_0); } else { DRM_ERROR("Failed to create rotation reflect property\n"); } } static void meson_video_create_video_cap_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_cap", 0, 0); if (prop) { video_plane->video_cap_property = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_cap property\n"); } } static void meson_video_create_video_src_min_size_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_min_size", 0, 0); if (prop) { video_plane->video_src_min_size_property = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_min_size property\n"); } } static void meson_video_create_video_src_max_size_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_max_size", 0, 0); if (prop) { video_plane->video_src_max_size_property = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_max_size property\n"); } } struct drm_property * meson_create_signal_fmt_prop(struct drm_device *dev, unsigned int supported_filters) { struct drm_property *prop; static const struct drm_prop_enum_list props[] = { { SIGNAL_FMT_SDR, "sdr" }, { SIGNAL_FMT_HDR10, "hdr10" }, { SIGNAL_FMT_HDR10PRIME, "hdr10prime" }, { SIGNAL_FMT_CUVA_HDR, "cuva_hdr" }, }; unsigned int valid_mode_mask = BIT(SIGNAL_FMT_SDR) | BIT(SIGNAL_FMT_HDR10) | BIT(SIGNAL_FMT_HDR10PRIME) | BIT(SIGNAL_FMT_CUVA_HDR); int i; if (WARN_ON((supported_filters & ~valid_mode_mask) || ((supported_filters & BIT(VFRAME_SIGNAL_FMT_SDR)) == 0))) return ERR_PTR(-EINVAL); prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, "signal_format", hweight32(supported_filters)); if (!prop) return ERR_PTR(-ENOMEM); for (i = 0; i < ARRAY_SIZE(props); i++) { int ret; if (!(BIT(props[i].type) & supported_filters)) continue; ret = drm_property_add_enum(prop, props[i].type, props[i].name); if (ret) { drm_property_destroy(dev, prop); return ERR_PTR(ret); } } return prop; } int meson_video_plane_create_signal_fmt_property(struct am_video_plane *video_plane, unsigned int supported_filters) { struct drm_property *prop = meson_create_signal_fmt_prop(video_plane->base.dev, supported_filters); if (IS_ERR(prop)) return PTR_ERR(prop); drm_object_attach_property(&video_plane->base.base, prop, VFRAME_SIGNAL_FMT_SDR); video_plane->signal_fmt_property = prop; return 0; } static int meson_video_plane_create_in_fence_property(struct meson_drm *priv, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_signed_range(priv->drm, DRM_MODE_PROP_ATOMIC, "ASYNC_IN_FENCE_FD", -1, INT_MAX); if (!prop) return -ENOMEM; drm_object_attach_property(&video_plane->base.base, prop, -1); video_plane->async_in_fence_property = prop; return 0; } static void meson_video_create_video_present_fence_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_present_fence", 0, U64_MAX);// if (prop) { video_plane->video_present_fence_property = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_present_fence property\n"); } } static void meson_video_create_video_release_fence_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_release_fence", 0, U64_MAX);// if (prop) { video_plane->video_release_fence_property = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_present_fence property\n"); } } static void meson_video_create_frame_flag_property(struct drm_device *drm_dev, struct am_video_plane *video_plane) { struct drm_property *prop; prop = drm_property_create_range(drm_dev, 0, "video_frame_flag", 0, UINT_MAX); if (prop) { video_plane->frame_flag_prop = prop; drm_object_attach_property(&video_plane->base.base, prop, 0); } else { DRM_ERROR("Failed to create video_frame_flag property\n"); } } static int meson_plane_create_dimm_ctrl_property(struct am_osd_plane *osd_plane, unsigned int bgcolor, unsigned int min, unsigned int max) { struct drm_property *prop; prop = drm_property_create_range(osd_plane->base.dev, 0, "OSD_DIMMING_CTRL", min, max); if (!prop) return -ENOMEM; drm_object_attach_property(&osd_plane->base.base, prop, bgcolor); osd_plane->dimm_ctrl_property = prop; return 0; } static void meson_plane_get_primary_plane(struct meson_drm *priv, enum drm_plane_type *type) { int i, j; for (j = 0; j < MESON_MAX_OSDS; j++) type[j] = DRM_PLANE_TYPE_OVERLAY; for (i = 0; i < MESON_MAX_CRTC; i++) { for (j = 0; j < MESON_MAX_OSDS; j++) { if (j < MESON_MAX_OSDS && i == priv->of_conf.crtcmask_osd[j] && priv->osd_occupied_index != j) { type[j] = DRM_PLANE_TYPE_PRIMARY; priv->primary_plane_index[i] = j; break; } } } } static struct am_osd_plane *am_osd_plane_create(struct meson_drm *priv, int i, u32 crtc_mask, enum drm_plane_type type) { struct am_osd_plane *osd_plane; struct meson_of_conf *conf; struct drm_plane *plane; u32 zpos, min_zpos, max_zpos, index, osd_reverse; char plane_name[8]; const u32 *formats_group; int num_formats; const char *const_plane_name; struct drm_rect adjust_rect = {-1, -1, -1, -1}; osd_plane = devm_kzalloc(priv->drm->dev, sizeof(*osd_plane), GFP_KERNEL); if (!osd_plane) return 0; min_zpos = OSD_PLANE_BEGIN_ZORDER; max_zpos = OSD_PLANE_END_ZORDER; osd_plane->drv = priv; osd_plane->plane_index = i; osd_plane->plane_type = OSD_PLANE; osd_plane->adjust_src = adjust_rect; osd_plane->adjust_dst = adjust_rect; conf = &priv->of_conf; osd_reverse = DRM_MODE_ROTATE_0; index = 0; #ifdef CONFIG_AMLOGIC_MEDIA_FB get_logo_osd_reverse(&index, &osd_reverse); #else drm_logo_get_osd_reverse(&index, &osd_reverse); #endif if (index == osd_plane->plane_index || index == DEV_ALL) { switch (osd_reverse) { case 1: osd_plane->osd_reverse = DRM_MODE_REFLECT_MASK; break; case 2: osd_plane->osd_reverse = DRM_MODE_REFLECT_X; break; case 3: osd_plane->osd_reverse = DRM_MODE_REFLECT_Y; break; default: osd_plane->osd_reverse = DRM_MODE_ROTATE_0; break; } } zpos = osd_plane->plane_index + min_zpos; plane = &osd_plane->base; sprintf(plane_name, "osd%d", i); const_plane_name = plane_name; if (i == priv->osd_occupied_index) osd_plane->osd_occupied = true; else osd_plane->osd_occupied = false; if (!priv->vpu_data->osd_formats) { formats_group = supported_drm_formats; num_formats = ARRAY_SIZE(supported_drm_formats); } else { formats_group = priv->vpu_data->osd_formats->formats; num_formats = priv->vpu_data->osd_formats->format_num; } if (conf->osd_afbc_mask & BIT(i)) { drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask, &am_osd_plane_funs, formats_group, num_formats, afbc_modifier, type, const_plane_name); } else { priv->drm->mode_config.fb_modifiers_not_supported = !false; drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask, &am_osd_plane_funs, formats_group, num_formats, NULL, type, const_plane_name); priv->drm->mode_config.fb_modifiers_not_supported = !true; } drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE)); drm_plane_create_alpha_property(plane); drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_MASK); drm_plane_create_zpos_property(plane, zpos, min_zpos, max_zpos); drm_plane_helper_add(plane, &am_osd_helper_funcs); meson_plane_create_scaling_filter_property(plane, BIT(DRM_SCALING_FILTER_DEFAULT) | BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR) | BIT(DRM_SCALING_FILTER_BICUBIC_SHARP) | BIT(DRM_SCALING_FILTER_BICUBIC) | BIT(DRM_SCALING_FILTER_BILINEAR) | BIT(DRM_SCALING_FILTER_2POINT_BILINEAR) | BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE_SHARP) | BIT(DRM_SCALING_FILTER_3POINT_TRIANGLE) | BIT(DRM_SCALING_FILTER_4POINT_TRIANGLE) | BIT(DRM_SCALING_FILTER_4POINT_BSPLINE) | BIT(DRM_SCALING_FILTER_3POINT_BSPLINE) | BIT(DRM_SCALING_FILTER_REPEATE)); meson_plane_add_occupied_property(priv->drm, osd_plane); meson_plane_add_max_fb_property(priv->drm, osd_plane); meson_plane_add_osd_capability_property(priv->drm, osd_plane); meson_plane_add_rotation_reflect_property(priv->drm, osd_plane); DRM_INFO("osd plane %d create done,occupied-%d crtc_mask-%d type-%d reverse-%d zpos-%d\n", i, osd_plane->osd_occupied, crtc_mask, type, osd_reverse, zpos); meson_plane_create_security_en_property(priv->drm, osd_plane); meson_plane_add_palette_property(priv->drm, osd_plane); meson_plane_add_unsupport_nonafbc_property(priv->drm, osd_plane); meson_plane_create_dimm_ctrl_property(osd_plane, 0, 0, 0xffffffff); return osd_plane; } static struct am_video_plane *am_video_plane_create(struct meson_drm *priv, int i, u32 crtc_mask) { struct am_video_plane *video_plane; struct drm_plane *plane; const u32 *formats_group; int num_formats; char plane_name[8]; u32 zpos, min_zpos, max_zpos; const char *const_plane_name; struct drm_rect adjust_rect = {-1, -1, -1, -1}; video_plane = devm_kzalloc(priv->drm->dev, sizeof(*video_plane), GFP_KERNEL); if (!video_plane) { DRM_INFO("no memory to alloc video plane\n"); return 0; } video_plane->context = dma_fence_context_alloc(1); video_plane->seqno = 0; video_plane->present_context = dma_fence_context_alloc(1); video_plane->present_seqno = 0; min_zpos = VIDEO_PLANE_BEGIN_ZORDER; max_zpos = VIDEO_PLANE_END_ZORDER; video_plane->drv = priv; video_plane->plane_index = i; video_plane->plane_type = VIDEO_PLANE; video_plane->pipeline = priv->pipeline; video_plane->adjust_src = adjust_rect; video_plane->adjust_dst = adjust_rect; zpos = video_plane->plane_index + min_zpos; plane = &video_plane->base; sprintf(plane_name, "video%d", i); const_plane_name = plane_name; if (!priv->vpu_data->video_formats) { formats_group = video_supported_drm_formats; num_formats = ARRAY_SIZE(video_supported_drm_formats); } else { formats_group = priv->vpu_data->video_formats->formats; num_formats = priv->vpu_data->video_formats->format_num; } drm_universal_plane_init(priv->drm, plane, 1 << crtc_mask, &am_video_plane_funs, formats_group, num_formats, video_fbc_modifier, DRM_PLANE_TYPE_OVERLAY, const_plane_name); drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, DRM_MODE_ROTATE_MASK | DRM_MODE_REFLECT_MASK); drm_plane_create_zpos_property(plane, zpos, min_zpos, max_zpos); meson_video_plane_create_in_fence_property(priv, video_plane); meson_video_create_video_present_fence_property(priv->drm, video_plane); meson_video_create_video_release_fence_property(priv->drm, video_plane); meson_video_plane_create_signal_fmt_property(video_plane, BIT(SIGNAL_FMT_SDR) | BIT(SIGNAL_FMT_HDR10) | BIT(SIGNAL_FMT_HDR10PRIME) | BIT(SIGNAL_FMT_CUVA_HDR)); meson_video_create_frame_flag_property(priv->drm, video_plane); drm_plane_helper_add(plane, &am_video_helper_funcs); meson_video_create_video_cap_property(priv->drm, video_plane); meson_video_create_video_src_min_size_property(priv->drm, video_plane); meson_video_create_video_src_max_size_property(priv->drm, video_plane); spin_lock_init(&video_plane->fence_lock); spin_lock_init(&video_plane->present_fence_lock); DRM_INFO("video plane %d create done\n", i); return video_plane; } static void register_osd_status_cb_func(void) { #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM #ifndef CONFIG_AMLOGIC_ZAPPER_CUT register_osd_status_cb(get_osd_status_callback); #endif #endif #ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION register_osd_func(get_osd_status_callback); #endif } int am_meson_plane_create(struct meson_drm *priv) { struct am_osd_plane *plane; struct am_video_plane *video_plane; struct meson_vpu_pipeline *pipeline = priv->pipeline; struct meson_of_conf *conf = &priv->of_conf; enum drm_plane_type type[MESON_MAX_OSD]; int i, osd_index, video_index; u32 num_osd_plane = 0; memset(priv->osd_planes, 0, sizeof(struct am_osd_plane *) * MESON_MAX_OSD); memset(priv->video_planes, 0, sizeof(struct am_video_plane *) * MESON_MAX_VIDEO); /*calculate primary plane*/ meson_plane_get_primary_plane(priv, type); register_osd_status_cb_func(); /*osd plane*/ for (i = 0; i < MESON_MAX_OSD; i++) { if (!pipeline->osds[i]) continue; osd_index = pipeline->osds[i]->base.index; plane = am_osd_plane_create(priv, osd_index, conf->crtcmask_osd[i], type[i]); if (!plane) return -ENOMEM; if (i == 0) priv->primary_plane = &plane->base; priv->osd_planes[i] = plane; priv->num_planes++; } num_osd_plane = priv->num_planes; /*video plane: init after osd to provide osd id at first.*/ for (i = 0; i < pipeline->num_video; i++) { video_index = pipeline->video[i]->base.index; video_plane = am_video_plane_create(priv, video_index, conf->crtcmask_video[i]); if (!video_plane) return -ENOMEM; priv->video_planes[i] = video_plane; priv->num_planes++; } DRM_INFO("%u osd and %u video plane are created\n", num_osd_plane, priv->num_planes - num_osd_plane); return 0; }