| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include "meson_plane.h" |
| #include "meson_crtc.h" |
| #include "meson_vpu.h" |
| #include "meson_drv.h" |
| #include "meson_vpu_pipeline.h" |
| #include "meson_osd_afbc.h" |
| #include "meson_gem.h" |
| |
| |
| 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_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_LINEAR, |
| DRM_FORMAT_MOD_INVALID |
| }; |
| |
| static const u32 supported_drm_formats[] = { |
| 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_RGB565, |
| DRM_FORMAT_ARGB1555, |
| DRM_FORMAT_ARGB4444, |
| }; |
| |
| 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 |
| }; |
| |
| static const u32 video_supported_drm_formats[] = { |
| DRM_FORMAT_NV12, |
| DRM_FORMAT_NV21, |
| DRM_FORMAT_YUYV, |
| DRM_FORMAT_YVYU, |
| DRM_FORMAT_YUV444, |
| DRM_FORMAT_YUV422, |
| DRM_FORMAT_YUV420, |
| DRM_FORMAT_YUV411, |
| DRM_FORMAT_YUV410, |
| DRM_FORMAT_YUV420_8BIT, |
| DRM_FORMAT_YUV420_10BIT, |
| }; |
| |
| 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 = pipeline->crtc; |
| unsigned int index = drm_crtc_index(crtc); |
| struct drm_crtc_state *crtc_state = atomic_state->crtcs[index].state; |
| struct drm_display_mode *mode = &crtc_state->mode; |
| |
| if (!crtc_state || !mode) |
| mode = &pipeline->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->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->rotation = state->rotation; |
| if (state->plane) { |
| amp = to_am_osd_plane(state->plane); |
| 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 (scan_mode_out) { |
| plane_info->dst_y >>= 1; |
| plane_info->dst_h >>= 1; |
| } |
| /*negative position process*/ |
| if (state->crtc_x < 0) { |
| dst_w = state->crtc_w + state->crtc_x; |
| if (dst_w > 0) { |
| src_w = plane_info->src_w * dst_w / state->crtc_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; |
| } |
| } |
| if (state->crtc_y < 0) { |
| dst_h = state->crtc_h + state->crtc_y; |
| if (dst_h > 0) { |
| src_h = plane_info->src_h * dst_h / state->crtc_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; |
| } |
| } |
| /*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; |
| } |
| } |
| 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; |
| } |
| } |
| /*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; |
| } |
| |
| 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 dst_w, dst_h, src_w, src_h; |
| struct drm_atomic_state *atomic_state = state->state; |
| struct drm_crtc *crtc = pipeline->crtc; |
| unsigned int index = drm_crtc_index(crtc); |
| struct drm_crtc_state *crtc_state = atomic_state->crtcs[index].state; |
| struct drm_display_mode *mode = &crtc_state->mode; |
| |
| if (!crtc_state || !mode) |
| mode = &pipeline->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->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; |
| /*negative position process*/ |
| if (state->crtc_x < 0) { |
| dst_w = state->crtc_w + state->crtc_x; |
| if (dst_w > 0) { |
| src_w = plane_info->src_w * dst_w / state->crtc_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; |
| } |
| } |
| if (state->crtc_y < 0) { |
| dst_h = state->crtc_h + state->crtc_y; |
| if (dst_h > 0) { |
| src_h = plane_info->src_h * dst_h / state->crtc_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; |
| } |
| } |
| /*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 |
| plane_info->dst_w = |
| mode->hdisplay - plane_info->dst_x; |
| } |
| if ((plane_info->dst_y + plane_info->dst_h) > mode->vdisplay) { |
| if (plane_info->dst_y >= mode->vdisplay) |
| plane_info->enable = 0; |
| else |
| plane_info->dst_h = mode->vdisplay - plane_info->dst_y; |
| } |
| DRM_DEBUG("mode->hdisplay=%d, mode->vdisplay=%d\n", |
| mode->hdisplay, mode->vdisplay); |
| } |
| |
| static int |
| meson_plane_check_size_range(struct meson_vpu_osd_layer_info *plane_info) |
| { |
| u32 dst_w, dst_h, src_w, src_h, ratio_x, ratio_y; |
| int ret; |
| |
| src_w = plane_info->src_w; |
| src_h = plane_info->src_h; |
| dst_w = plane_info->dst_w; |
| dst_h = plane_info->dst_h; |
| ratio_x = 0; |
| ratio_y = 0; |
| ret = 0; |
| |
| if (src_w > dst_w) |
| ratio_x = (src_w + dst_w - 1) / dst_w; |
| if (src_h > dst_h) |
| ratio_y = (src_h + dst_h - 1) / dst_h; |
| if (ratio_x > MESON_OSD_SCLAE_DOWN_LIMIT || |
| ratio_y > MESON_OSD_SCLAE_DOWN_LIMIT) |
| ret = -EDOM; |
| if (src_w < dst_w) |
| ratio_x = (dst_w + src_w - 1) / src_w; |
| if (src_h < dst_h) |
| ratio_y = (dst_h + src_h - 1) / src_h; |
| if (ratio_x > MESON_OSD_SCLAE_UP_LIMIT || |
| ratio_y > MESON_OSD_SCLAE_UP_LIMIT) |
| ret = -EDOM; |
| return ret; |
| } |
| |
| 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_DRM_MESON_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_DRM_MESON_USE_ION |
| meson_fb = container_of(fb, struct am_meson_fb, base); |
| if (!meson_fb) { |
| return -EINVAL; |
| } |
| DRM_DEBUG("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; |
| DRM_DEBUG("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); |
| } else { |
| phyaddr = 0; |
| DRM_INFO("don't find phyaddr!\n"); |
| return -EINVAL; |
| } |
| #else |
| if (!fb) { |
| 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 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_DRM_MESON_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 uvm_buf_obj *ubo; |
| #else |
| struct drm_gem_cma_object *gem; |
| #endif |
| size_t fb_size[2] = {0}; |
| phys_addr_t phyaddr, phyaddr1 = 0; |
| |
| #ifdef CONFIG_DRM_MESON_USE_ION |
| meson_fb = container_of(fb, struct am_meson_fb, base); |
| if (!meson_fb) { |
| return -EINVAL; |
| } |
| DRM_DEBUG("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)) |
| phyaddr1 = am_meson_gem_object_get_phyaddr(drv, |
| meson_fb->bufp[1], |
| &fb_size[1]); |
| /* start to get vframe from uvm */ |
| if (meson_fb->bufp[0]->is_uvm) { |
| ubo = &meson_fb->bufp[0]->ubo; |
| plane_info->vf = dmabuf_get_vframe(ubo->dmabuf); |
| dmabuf_put_vframe(ubo->dmabuf); |
| plane_info->is_uvm = meson_fb->bufp[0]->is_uvm; |
| if (video_plane->vfm_mode) |
| plane_info->dmabuf = ubo->dmabuf; |
| DRM_DEBUG("%s dmabuf %px\n", __func__, ubo->dmabuf); |
| } |
| #else |
| if (!fb) { |
| 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->afbc_en = 0; |
| plane_info->afbc_inter_format = 0; |
| plane_info->fb_w = fb->width; |
| plane_info->fb_h = fb->height; |
| |
| /*setup afbc info*/ |
| if (fb->modifier) { |
| 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; |
| |
| DRM_DEBUG("flags:%d pixel_format:%d,modifer=%llu\n", |
| fb->flags, fb->format->format, |
| fb->modifier); |
| DRM_DEBUG("plane afbc_en=%u, afbc_inter_format=%x\n", |
| plane_info->afbc_en, plane_info->afbc_inter_format); |
| |
| DRM_DEBUG("phy_addr=0x%pa,byte_stride=%d,pixel_format=%d\n", |
| &plane_info->phy_addr, plane_info->byte_stride, |
| plane_info->pixel_format); |
| DRM_DEBUG("plane_index %d, size %d.\n", |
| osd_plane->plane_index, |
| plane_info->fb_size); |
| 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]; |
| |
| DRM_DEBUG("flags:%d pixel_format:%d,modifer=%llu\n", |
| fb->flags, fb->format->format, |
| fb->modifier); |
| DRM_DEBUG("phy_addr[0]=0x%pa,byte_stride=%d,pixel_format=%d\n", |
| &plane_info->phy_addr[0], plane_info->byte_stride, |
| plane_info->pixel_format); |
| DRM_DEBUG("phy_addr[1]=0x%pa, plane_index %d, size-0 %d, size-1 %d.\n", |
| &plane_info->phy_addr[1], |
| video_plane->plane_index, |
| plane_info->fb_size[0], |
| plane_info->fb_size[1]); |
| return 0; |
| } |
| |
| static u32 meson_video_parse_config(struct drm_device *dev) |
| { |
| u32 mode_flag = 0; |
| int ret; |
| |
| ret = of_property_read_u32(dev->dev->of_node, |
| "vfm_mode", &mode_flag); |
| if (ret) |
| DRM_INFO("%s parse vfm mode fail!\n", __func__); |
| |
| DRM_INFO("vfm_mode=%d\n", mode_flag); |
| return mode_flag; |
| } |
| |
| static const char *am_meson_video_fence_get_driver_name(struct dma_fence *fence) |
| { |
| return "meson"; |
| } |
| |
| static const char * |
| am_meson_video_fence_get_timeline_name(struct dma_fence *fence) |
| { |
| return "meson_video_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, |
| }; |
| |
| static struct dma_fence *am_meson_video_create_fence(spinlock_t *lock) |
| { |
| 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, 0, 0); |
| |
| return fence; |
| } |
| |
| 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; |
| struct dma_buf *dmabuf; |
| struct drm_framebuffer *fb = state->fb; |
| struct am_meson_fb *meson_fb; |
| int ret = 0; |
| |
| #ifdef CONFIG_DRM_MESON_USE_ION |
| meson_fb = container_of(fb, struct am_meson_fb, base); |
| if (!meson_fb) |
| return -EINVAL; |
| |
| fence = am_meson_video_create_fence(&video_plane->lock); |
| if (!fence) |
| return -ENOMEM; |
| /*need wait on the in_fence(explicit fence) |
| */ |
| |
| /*creat implicit fence as out_fence, and next will directly |
| *export to video composer module |
| */ |
| dmabuf = meson_fb->bufp[0]->ubo.dmabuf; |
| dma_resv_add_excl_fence(dmabuf->resv, fence); |
| |
| ret = dma_resv_reserve_shared(dmabuf->resv, 1); |
| if (unlikely(ret)) |
| return ret; |
| dma_resv_add_shared_fence(dmabuf->resv, fence); |
| mvv->fence = fence; |
| |
| DRM_DEBUG("creat fence %s fence(%px) plane_index%d, dmabuf(%px)\n", |
| __func__, fence, video_plane->plane_index, dmabuf); |
| #endif |
| return 0; |
| } |
| |
| static int meson_plane_atomic_get_property(struct drm_plane *plane, |
| const struct drm_plane_state *state, |
| struct drm_property *property, |
| uint64_t *val) |
| { |
| DRM_DEBUG("Not supported prop [%s]", property->name); |
| return -EINVAL; |
| } |
| |
| static int meson_plane_atomic_set_property(struct drm_plane *plane, |
| struct drm_plane_state *state, |
| struct drm_property *property, |
| uint64_t val) |
| { |
| /*nothing to do now*/ |
| DRM_DEBUG("Not supported prop [%s]", property->name); |
| return -EINVAL; |
| } |
| |
| 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("%s (%s)\n", __func__, 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_plane_reset(struct drm_plane *plane) |
| { |
| struct am_meson_plane_state *meson_plane_state; |
| |
| 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; |
| } |
| |
| bool am_meson_vpu_check_format_mod(struct drm_plane *plane, |
| u32 format, u64 modifier) |
| { |
| DRM_DEBUG("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: |
| 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("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_KMS("%llx invalid format 0x%08x\n", |
| modifier, format); |
| return false; |
| } |
| |
| if (layout != AMLOGIC_FBC_LAYOUT_BASIC && |
| layout != AMLOGIC_FBC_LAYOUT_SCATTER) { |
| DRM_DEBUG_KMS("%llx invalid layout %x\n", |
| modifier, layout); |
| return false; |
| } |
| |
| if (options && |
| options != AMLOGIC_FBC_OPTION_MEM_SAVING) { |
| DRM_DEBUG_KMS("%llx invalid layout %x\n", |
| modifier, layout); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| DRM_DEBUG_KMS("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_pipeline_state *mvps; |
| struct am_osd_plane *osd_plane; |
| struct meson_drm *drv; |
| struct drm_private_state *obj_state; |
| |
| 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("%s [%d]\n", __func__, osd_plane->plane_index); |
| |
| drv = osd_plane->drv; |
| if (!drv || !drv->pipeline) { |
| DRM_INFO("%s private state or pipeline is NULL!\n", __func__); |
| return; |
| } |
| |
| obj_state = drv->pipeline->obj.state; |
| if (!obj_state) { |
| DRM_ERROR("null pipeline obj state!\n"); |
| return; |
| } |
| |
| mvps = container_of(obj_state, struct meson_vpu_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\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_w=%u\n", plane_info->dst_w); |
| drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_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\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\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); |
| } |
| |
| 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_vpu_pipeline_state *mvps; |
| struct am_video_plane *video_plane; |
| struct meson_drm *drv; |
| struct drm_private_state *obj_state; |
| |
| 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("%s [%d]\n", __func__, video_plane->plane_index); |
| |
| drv = video_plane->drv; |
| if (!drv || !drv->pipeline) { |
| DRM_INFO("%s private state is NULL!\n", __func__); |
| return; |
| } |
| |
| obj_state = drv->pipeline->obj.state; |
| if (!obj_state) { |
| DRM_ERROR("null pipeline obj state!\n"); |
| return; |
| } |
| |
| mvps = container_of(obj_state, struct meson_vpu_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\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_w=%u\n", plane_info->dst_w); |
| drm_printf(p, "\t\tdst_h=%u\n", plane_info->dst_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\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); |
| drm_printf(p, "\t\tvfm_mode=%u\n", plane_info->vfm_mode); |
| } |
| |
| 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_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_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_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_plane_state *old_state) |
| { |
| DRM_DEBUG("osd plane atomic_update.\n"); |
| } |
| |
| static void meson_video_plane_atomic_update(struct drm_plane *plane, |
| struct drm_plane_state *old_state) |
| { |
| 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->pipeline; |
| struct drm_atomic_state *old_atomic_state = old_state->state; |
| struct meson_vpu_video *mvv = pipeline->video[video_plane->plane_index]; |
| |
| if (video_plane->vfm_mode) |
| meson_video_prepare_fence(plane, old_state, mvv); |
| |
| DRM_DEBUG("video plane atomic_update.\n"); |
| vpu_video_plane_update(pipeline, old_atomic_state, video_plane->plane_index); |
| } |
| |
| static int meson_plane_atomic_check(struct drm_plane *plane, |
| struct drm_plane_state *state) |
| { |
| struct meson_vpu_osd_layer_info *plane_info; |
| struct meson_vpu_pipeline_state *mvps; |
| struct am_osd_plane *osd_plane = to_am_osd_plane(plane); |
| struct meson_drm *drv = osd_plane->drv; |
| int ret; |
| |
| if (!state || !drv) { |
| DRM_INFO("%s state/meson_drm is NULL!\n", __func__); |
| return -EINVAL; |
| } |
| |
| DRM_DEBUG("%s [%d]\n", __func__, osd_plane->plane_index); |
| |
| mvps = meson_vpu_pipeline_get_state(drv->pipeline, state->state); |
| 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; |
| /*get plane prop value*/ |
| plane_info->zorder = state->zpos; |
| plane_info->pixel_blend = state->pixel_blend_mode; |
| plane_info->global_alpha = state->alpha; |
| plane_info->scaling_filter = (u32)state->scaling_filter; |
| |
| mvps->plane_index[osd_plane->plane_index] = osd_plane->plane_index; |
| meson_plane_position_calc(plane_info, state, mvps->pipeline); |
| ret = meson_plane_check_size_range(plane_info); |
| if (ret < 0) { |
| plane_info->enable = 0; |
| DRM_INFO("plane%d size check unsupport!!!\n", |
| plane_info->plane_index); |
| return ret; |
| } |
| ret = meson_plane_fb_check(plane, state, plane_info); |
| if (ret < 0) { |
| plane_info->enable = 0; |
| DRM_DEBUG("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("fb is invalid, disable plane[%d].\n", plane_info->src_w); |
| return ret; |
| } |
| |
| plane_info->enable = 1; |
| DRM_DEBUG("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("w/h = %d/%d, src_x/y/w/h=%d/%d/%d/%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); |
| DRM_DEBUG("dst_x/y/w/h=%d/%d/%d/%d\n", |
| 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_plane_state *state) |
| { |
| struct meson_vpu_video_layer_info *plane_info; |
| struct meson_vpu_pipeline_state *mvps; |
| struct am_video_plane *video_plane = to_am_video_plane(plane); |
| struct meson_drm *drv = video_plane->drv; |
| int ret; |
| |
| if (!state || !drv) { |
| DRM_INFO("%s state/meson_drm is NULL!\n", __func__); |
| return -EINVAL; |
| } |
| |
| DRM_DEBUG("planeidx [%d]\n", video_plane->plane_index); |
| |
| mvps = meson_vpu_pipeline_get_state(drv->pipeline, state->state); |
| if (!mvps || video_plane->plane_index >= MESON_MAX_VIDEO) { |
| DRM_INFO("%s mvps/video_plane is NULL!\n", __func__); |
| return -EINVAL; |
| } |
| |
| plane_info = &mvps->video_plane_info[video_plane->plane_index]; |
| plane_info->plane_index = video_plane->plane_index; |
| plane_info->vfm_mode = video_plane->vfm_mode; |
| plane_info->zorder = state->zpos + plane_info->plane_index; |
| |
| mvps->plane_index[video_plane->plane_index] = video_plane->plane_index; |
| meson_video_plane_position_calc(plane_info, state, |
| mvps->pipeline); |
| ret = meson_video_plane_fb_check(plane, state, plane_info); |
| if (ret < 0) { |
| plane_info->enable = 0; |
| DRM_DEBUG("plane%d fb is NULL,disable the plane!\n", |
| plane_info->plane_index); |
| return 0; |
| } |
| ret = meson_video_plane_get_fb_info(plane, state, plane_info); |
| if (ret < 0 || |
| plane_info->src_w == 0) { |
| plane_info->enable = 0; |
| return ret; |
| } |
| |
| plane_info->enable = 1; |
| DRM_DEBUG("VIDOE PLANE index=%d, zorder=%d\n", |
| plane_info->plane_index, plane_info->zorder); |
| DRM_DEBUG("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("dst_x/y/w/h=%d/%d/%d/%d\n", |
| 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_plane_state *old_state) |
| { |
| struct am_osd_plane *osd_plane = to_am_osd_plane(plane); |
| |
| DRM_DEBUG("%s osd %d.\n", __func__, osd_plane->plane_index); |
| } |
| |
| static void meson_video_plane_atomic_disable(struct drm_plane *plane, |
| struct drm_plane_state *old_state) |
| { |
| struct am_video_plane *video_plane = to_am_video_plane(plane); |
| struct meson_vpu_pipeline *pipeline = video_plane->pipeline; |
| struct drm_atomic_state *old_atomic_state = old_state->state; |
| |
| DRM_DEBUG("%s video %d.\n", __func__, video_plane->plane_index); |
| vpu_video_plane_update(pipeline, old_atomic_state, video_plane->plane_index); |
| } |
| |
| /*add async check & atomic funs*/ |
| int meson_osd_plane_async_check(struct drm_plane *plane, |
| struct drm_plane_state *new_state) |
| { |
| int ret; |
| struct meson_vpu_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; |
| |
| if (!new_state->state) { |
| DRM_ERROR("atomic state is NULL!\n"); |
| return -EINVAL; |
| } |
| |
| mvps = meson_vpu_pipeline_get_state(drv->pipeline, new_state->state); |
| 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("plane visible state changed.\n"); |
| return -EINVAL; |
| } |
| |
| if ((plane_info->src_w != ((new_state->src_w >> 16) & 0xffff)) || |
| (plane_info->src_h != ((new_state->src_h >> 16) & 0xffff)) || |
| plane_info->dst_x != new_state->crtc_x || |
| plane_info->dst_y != new_state->crtc_y || |
| plane_info->dst_w != new_state->crtc_w || |
| plane_info->dst_h != new_state->crtc_h || |
| plane_info->zorder != new_state->zpos) { |
| DRM_ERROR("plane info changed\n"); |
| return -EINVAL; |
| } |
| |
| ret = meson_plane_atomic_check(plane, new_state); |
| if (ret != 0) { |
| DRM_ERROR("meson_plane_atomic_check check error [%d].\n", ret); |
| return ret; |
| } |
| |
| ret = vpu_pipeline_osd_check(drv->pipeline, new_state->state); |
| if (ret != 0) |
| DRM_ERROR("vpu_pipeline_check check error [%d].\n", ret); |
| |
| return ret; |
| } |
| |
| int meson_video_plane_async_check(struct drm_plane *plane, |
| struct drm_plane_state *new_state) |
| { |
| int ret; |
| struct am_video_plane *video_plane = to_am_video_plane(plane); |
| struct meson_drm *drv = video_plane->drv; |
| |
| /*Now Video always can support async commit.*/ |
| ret = meson_video_plane_atomic_check(plane, new_state); |
| if (ret) { |
| DRM_ERROR("meson_video_plane_atomic_check failed (%d)", ret); |
| return ret; |
| } |
| |
| ret = vpu_pipeline_video_check(drv->pipeline, new_state->state); |
| if (ret) |
| DRM_ERROR("vpu_video_pipeline_check_block failed (%d)", ret); |
| |
| return ret; |
| } |
| |
| void meson_osd_plane_async_update(struct drm_plane *plane, |
| struct drm_plane_state *old_state) |
| { |
| struct am_meson_crtc *amcrtc = to_am_meson_crtc(plane->crtc); |
| struct meson_vpu_pipeline *pipeline = amcrtc->pipeline; |
| |
| if (!old_state || !old_state->state) { |
| DRM_ERROR("plane or atomic state is null.\n"); |
| return; |
| } |
| |
| #ifdef CONFIG_AMLOGIC_MEDIA_RDMA |
| meson_vpu_line_check(plane->crtc->index, plane->crtc->mode.vdisplay, |
| plane->crtc->mode.vrefresh); |
| #endif |
| vpu_pipeline_osd_update(pipeline, old_state->state); |
| #ifdef CONFIG_AMLOGIC_MEDIA_RDMA |
| meson_vpu_reg_vsync_config(); |
| #endif |
| } |
| |
| void meson_video_plane_async_update(struct drm_plane *plane, |
| struct drm_plane_state *old_state) |
| { |
| struct am_video_plane *video_plane = to_am_video_plane(plane); |
| struct meson_drm *drv = video_plane->drv; |
| |
| vpu_pipeline_video_update(drv->pipeline, old_state->state); |
| } |
| |
| 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, |
| }; |
| |
| 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, |
| }; |
| |
| 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_BINILEAR, "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_BINILEAR) | |
| 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 struct am_osd_plane *am_osd_plane_create(struct meson_drm *priv, int i) |
| { |
| struct am_osd_plane *osd_plane; |
| struct drm_plane *plane; |
| enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; |
| u32 zpos, min_zpos, max_zpos; |
| char plane_name[8]; |
| const char *const_plane_name; |
| |
| osd_plane = devm_kzalloc(priv->drm->dev, sizeof(*osd_plane), |
| GFP_KERNEL); |
| if (!osd_plane) |
| return 0; |
| |
| if (i == 0) |
| type = DRM_PLANE_TYPE_PRIMARY; |
| else |
| type = DRM_PLANE_TYPE_OVERLAY; |
| |
| 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; |
| if (logo.osd_reverse) |
| osd_plane->osd_reverse = DRM_MODE_REFLECT_MASK; |
| else |
| osd_plane->osd_reverse = DRM_MODE_ROTATE_0; |
| zpos = osd_plane->plane_index + min_zpos; |
| |
| plane = &osd_plane->base; |
| sprintf(plane_name, "osd%d", i); |
| const_plane_name = plane_name; |
| |
| drm_universal_plane_init(priv->drm, plane, 0xFF, |
| &am_osd_plane_funs, |
| supported_drm_formats, |
| ARRAY_SIZE(supported_drm_formats), |
| afbc_modifier, |
| type, const_plane_name); |
| 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_BINILEAR) | |
| 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)); |
| DRM_INFO("osd plane %d create done\n", i); |
| return osd_plane; |
| } |
| |
| static struct am_video_plane *am_video_plane_create(struct meson_drm *priv, |
| int i) |
| { |
| struct am_video_plane *video_plane; |
| struct drm_plane *plane; |
| char plane_name[8]; |
| u32 zpos, min_zpos, max_zpos; |
| const char *const_plane_name; |
| |
| 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; |
| } |
| min_zpos = 0; |
| max_zpos = 255; |
| |
| video_plane->drv = priv; |
| video_plane->plane_index = i; |
| |
| video_plane->plane_type = VIDEO_PLANE; |
| video_plane->pipeline = priv->pipeline; |
| zpos = video_plane->plane_index + min_zpos; |
| |
| plane = &video_plane->base; |
| sprintf(plane_name, "video%d", i); |
| const_plane_name = plane_name; |
| if (video_plane->vfm_mode) |
| spin_lock_init(&video_plane->lock); |
| drm_universal_plane_init(priv->drm, plane, 0xFF, |
| &am_video_plane_funs, |
| video_supported_drm_formats, |
| ARRAY_SIZE(video_supported_drm_formats), |
| video_fbc_modifier, |
| DRM_PLANE_TYPE_OVERLAY, const_plane_name); |
| |
| drm_plane_create_zpos_property(plane, zpos, min_zpos, max_zpos); |
| drm_plane_helper_add(plane, &am_video_helper_funcs); |
| DRM_INFO("video plane %d create done\n", i); |
| return video_plane; |
| } |
| |
| 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; |
| int i, osd_index, video_index; |
| u32 vfm_mode; |
| |
| 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); |
| |
| /*osd plane*/ |
| for (i = 0; i < pipeline->num_osds; i++) { |
| osd_index = pipeline->osds[i]->base.index; |
| plane = am_osd_plane_create(priv, osd_index); |
| |
| if (!plane) |
| return -ENOMEM; |
| |
| if (i == 0) |
| priv->primary_plane = &plane->base; |
| |
| priv->osd_planes[i] = plane; |
| priv->num_planes++; |
| } |
| DRM_INFO("create %d osd plane done\n", pipeline->num_osds); |
| vfm_mode = meson_video_parse_config(priv->drm); |
| |
| /*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); |
| if (!video_plane) |
| return -ENOMEM; |
| |
| video_plane->vfm_mode = vfm_mode; |
| priv->video_planes[i] = video_plane; |
| priv->num_planes++; |
| } |
| DRM_INFO("create %d video plane done\n", pipeline->num_video); |
| |
| return 0; |
| } |