From 8dc3d77ba509d73a17d9fc77859990742601fdcd Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Thu, 6 Jun 2019 18:06:38 +0800 Subject: [PATCH] drm/rockchip: vop: move plane calculate to atomic_check Change-Id: Icb5ff0ae4290720e8b288f839df4c010eed72d18 Signed-off-by: Mark Yao Signed-off-by: Sandy Huang --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 349 +++++++++++++++++--- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 12 + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 38 ++- drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 4 +- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 166 +++++++--- drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 8 + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 18 + include/drm/drm_encoder.h | 3 +- 8 files changed, 514 insertions(+), 84 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index c996d5d4ec7e..c1d62e4f4ca7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -62,8 +62,17 @@ struct rockchip_drm_mode_set { int hdisplay; int vdisplay; int vrefresh; + int flags; + int crtc_hsync_end; + int crtc_vsync_end; + + int left_margin; + int right_margin; + int top_margin; + int bottom_margin; bool mode_changed; + bool ymirror; int ratio; }; @@ -108,24 +117,206 @@ static struct drm_connector *find_connector_by_node(struct drm_device *drm_dev, return NULL; } +static +struct drm_connector *find_connector_by_bridge(struct drm_device *drm_dev, + struct device_node *node) +{ + struct device_node *np_encoder, *np_connector = NULL; + struct drm_encoder *encoder; + struct drm_connector *connector = NULL; + struct device_node *port, *endpoint; + bool encoder_bridge = false; + bool found_connector = false; + struct drm_connector_list_iter conn_iter; + + np_encoder = of_graph_get_remote_port_parent(node); + if (!np_encoder || !of_device_is_available(np_encoder)) + goto err_put_encoder; + drm_for_each_encoder(encoder, drm_dev) { + if (encoder->port == np_encoder && encoder->bridge) { + encoder_bridge = true; + break; + } + } + if (!encoder_bridge) { + dev_err(drm_dev->dev, "can't found encoder bridge!\n"); + goto err_put_encoder; + } + port = of_graph_get_port_by_id(np_encoder, 1); + if (!port) { + dev_err(drm_dev->dev, "can't found port point!\n"); + goto err_put_encoder; + } + for_each_child_of_node(port, endpoint) { + np_connector = of_graph_get_remote_port_parent(endpoint); + if (!np_connector) { + dev_err(drm_dev->dev, + "can't found connector node, please init!\n"); + goto err_put_port; + } + if (!of_device_is_available(np_connector)) { + of_node_put(np_connector); + np_connector = NULL; + continue; + } else { + break; + } + } + if (!np_connector) { + dev_err(drm_dev->dev, "can't found available connector node!\n"); + goto err_put_port; + } + + drm_connector_list_iter_begin(drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->port == np_connector) { + drm_connector_list_iter_end(&conn_iter); + found_connector = true; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + + if (!found_connector) + connector = NULL; + + of_node_put(np_connector); +err_put_port: + of_node_put(port); +err_put_encoder: + of_node_put(np_encoder); + + return connector; +} + +static void rockchip_free_loader_memory(struct drm_device *drm) +{ + struct rockchip_drm_private *private = drm->dev_private; + struct rockchip_logo *logo; + void *start, *end; + + if (!private || !private->logo/* || --private->logo->count*/) + return; + + logo = private->logo; + start = phys_to_virt(logo->start); + end = phys_to_virt(logo->start + logo->size); + + if (private->domain) { + iommu_unmap(private->domain, logo->dma_addr, + logo->iommu_map_size); + drm_mm_remove_node(&logo->mm); + } else { + dma_unmap_sg(drm->dev, logo->sgt->sgl, + logo->sgt->nents, DMA_TO_DEVICE); + } + sg_free_table(logo->sgt); + memblock_free(logo->start, logo->size); + free_reserved_area(start, end, -1, "drm_logo"); + kfree(logo); + private->logo = NULL; +} + +static int init_loader_memory(struct drm_device *drm_dev) +{ + struct rockchip_drm_private *private = drm_dev->dev_private; + struct rockchip_logo *logo; + struct device_node *np = drm_dev->dev->of_node; + struct device_node *node; + unsigned long nr_pages; + struct page **pages; + struct sg_table *sgt; + phys_addr_t start, size; + struct resource res; + int i, ret; + + node = of_parse_phandle(np, "logo-memory-region", 0); + if (!node) + return -ENOMEM; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return ret; + start = res.start; + size = resource_size(&res); + if (!size) + return -ENOMEM; + + logo = kmalloc(sizeof(*logo), GFP_KERNEL); + if (!logo) + return -ENOMEM; + + logo->kvaddr = phys_to_virt(start); + nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + pages = kmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL); + if (!pages) + goto err_free_logo; + i = 0; + while (i < nr_pages) { + pages[i] = phys_to_page(start); + start += PAGE_SIZE; + i++; + } + sgt = drm_prime_pages_to_sg(pages, nr_pages); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_free_pages; + } + + if (private->domain) { + memset(&logo->mm, 0, sizeof(logo->mm)); + ret = drm_mm_insert_node_generic(&private->mm, &logo->mm, + size, PAGE_SIZE, + 0, 0); + if (ret < 0) { + DRM_ERROR("out of I/O virtual memory: %d\n", ret); + goto err_free_pages; + } + + logo->dma_addr = logo->mm.start; + + logo->iommu_map_size = iommu_map_sg(private->domain, + logo->dma_addr, sgt->sgl, + sgt->nents, IOMMU_READ); + if (logo->iommu_map_size < size) { + DRM_ERROR("failed to map buffer"); + ret = -ENOMEM; + goto err_remove_node; + } + } else { + dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + logo->dma_addr = sg_dma_address(sgt->sgl); + } + + logo->sgt = sgt; + logo->start = res.start; + logo->size = size; + logo->count = 1; + private->logo = logo; + + return 0; + +err_remove_node: + drm_mm_remove_node(&logo->mm); +err_free_pages: + kfree(pages); +err_free_logo: + kfree(logo); + + return ret; +} + static struct drm_framebuffer * get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node) { + struct rockchip_drm_private *private = drm_dev->dev_private; struct drm_mode_fb_cmd2 mode_cmd = { 0 }; - struct device_node *memory; - struct resource res; u32 val; int bpp; - memory = of_parse_phandle(node, "logo,mem", 0); - if (!memory) + if (WARN_ON(!private->logo)) return NULL; - if (of_address_to_resource(memory, 0, &res)) { - pr_err("%s: could not get bootram phy addr\n", __func__); - return NULL; - } - if (of_property_read_u32(node, "logo,offset", &val)) { pr_err("%s: failed to get logo,offset\n", __func__); return NULL; @@ -150,10 +341,24 @@ get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node) } bpp = val; - mode_cmd.pitches[0] = mode_cmd.width * bpp / 8; - mode_cmd.pixel_format = DRM_FORMAT_BGR888; + mode_cmd.pitches[0] = ALIGN(mode_cmd.width * bpp, 32) / 8; - return rockchip_fb_alloc(drm_dev, &mode_cmd, NULL, &res, 1); + switch (bpp) { + case 16: + mode_cmd.pixel_format = DRM_FORMAT_BGR565; + break; + case 24: + mode_cmd.pixel_format = DRM_FORMAT_BGR888; + break; + case 32: + mode_cmd.pixel_format = DRM_FORMAT_XRGB8888; + break; + default: + pr_err("%s: unsupported to logo bpp %d\n", __func__, bpp); + return NULL; + } + + return rockchip_fb_alloc(drm_dev, &mode_cmd, NULL, private->logo, 1); } static struct rockchip_drm_mode_set * @@ -164,6 +369,7 @@ of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route) struct drm_framebuffer *fb; struct drm_connector *connector; struct drm_crtc *crtc; + const char *string; u32 val; connect = of_parse_phandle(route, "connect", 0); @@ -176,6 +382,8 @@ of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route) crtc = find_crtc_by_node(drm_dev, connect); connector = find_connector_by_node(drm_dev, connect); + if (!connector) + connector = find_connector_by_bridge(drm_dev, connect); if (!crtc || !connector) { dev_warn(drm_dev->dev, "No available crtc or connector for display"); @@ -193,14 +401,41 @@ of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route) if (!of_property_read_u32(route, "video,vdisplay", &val)) set->vdisplay = val; + if (!of_property_read_u32(route, "video,crtc_hsync_end", &val)) + set->crtc_hsync_end = val; + + if (!of_property_read_u32(route, "video,crtc_vsync_end", &val)) + set->crtc_vsync_end = val; + if (!of_property_read_u32(route, "video,vrefresh", &val)) set->vrefresh = val; + if (!of_property_read_u32(route, "video,flags", &val)) + set->flags = val; + + if (!of_property_read_u32(route, "logo,ymirror", &val)) + set->ymirror = val; + + if (!of_property_read_u32(route, "overscan,left_margin", &val)) + set->left_margin = val; + + if (!of_property_read_u32(route, "overscan,right_margin", &val)) + set->right_margin = val; + + if (!of_property_read_u32(route, "overscan,top_margin", &val)) + set->top_margin = val; + + if (!of_property_read_u32(route, "overscan,bottom_margin", &val)) + set->bottom_margin = val; + + set->ratio = 1; + if (!of_property_read_string(route, "logo,mode", &string) && + !strcmp(string, "fullscreen")) + set->ratio = 0; + set->fb = fb; set->crtc = crtc; set->connector = connector; - /* TODO: set display fullscreen or center */ - set->ratio = 0; return set; } @@ -407,12 +642,13 @@ static int update_state(struct drm_device *drm_dev, static void show_loader_logo(struct drm_device *drm_dev) { - struct drm_atomic_state *state; + struct drm_atomic_state *state, *old_state; struct device_node *np = drm_dev->dev->of_node; struct drm_mode_config *mode_config = &drm_dev->mode_config; struct device_node *root, *route; - struct rockchip_drm_mode_set *set, *tmp; + struct rockchip_drm_mode_set *set, *tmp, *unset; struct list_head mode_set_list; + struct list_head mode_unset_list; unsigned int plane_mask = 0; int ret; @@ -422,7 +658,13 @@ static void show_loader_logo(struct drm_device *drm_dev) return; } + if (init_loader_memory(drm_dev)) { + dev_warn(drm_dev->dev, "failed to parse loader memory\n"); + return; + } + INIT_LIST_HEAD(&mode_set_list); + INIT_LIST_HEAD(&mode_unset_list); drm_modeset_lock_all(drm_dev); state = drm_atomic_state_alloc(drm_dev); if (!state) { @@ -439,13 +681,44 @@ static void show_loader_logo(struct drm_device *drm_dev) if (setup_initial_state(drm_dev, state, set)) { drm_framebuffer_put(set->fb); - kfree(set); + INIT_LIST_HEAD(&set->head); + list_add_tail(&set->head, &mode_unset_list); continue; } INIT_LIST_HEAD(&set->head); list_add_tail(&set->head, &mode_set_list); } + /* + * the mode_unset_list store the unconnected route, if route's crtc + * isn't used, we should close it. + */ + list_for_each_entry_safe(unset, tmp, &mode_unset_list, head) { + struct rockchip_drm_mode_set *tmp_set; + int find_used_crtc = 0; + + list_for_each_entry_safe(set, tmp_set, &mode_set_list, head) { + if (set->crtc == unset->crtc) { + find_used_crtc = 1; + continue; + } + } +#if 0 +//todo + if (!find_used_crtc) { + struct drm_crtc *crtc = unset->crtc; + int pipe = drm_crtc_index(crtc); + struct rockchip_drm_private *priv = + drm_dev->dev_private; + + if (unset->hdisplay && unset->vdisplay) + priv->crtc_funcs[pipe]->crtc_close(crtc); + } +#endif + list_del(&unset->head); + kfree(unset); + } + if (list_empty(&mode_set_list)) { dev_warn(drm_dev->dev, "can't not find any loader display\n"); ret = -ENXIO; @@ -459,12 +732,20 @@ static void show_loader_logo(struct drm_device *drm_dev) */ WARN_ON(drm_atomic_helper_swap_state(state, false)); drm_atomic_state_put(state); + old_state = drm_atomic_helper_duplicate_state(drm_dev, + mode_config->acquire_ctx); + if (IS_ERR(old_state)) { + dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); + ret = PTR_ERR_OR_ZERO(old_state); + goto err_free_state; + } + state = drm_atomic_helper_duplicate_state(drm_dev, mode_config->acquire_ctx); if (IS_ERR(state)) { dev_err(drm_dev->dev, "failed to duplicate atomic state\n"); ret = PTR_ERR_OR_ZERO(state); - goto err_unlock; + goto err_free_old_state; } state->acquire_ctx = mode_config->acquire_ctx; list_for_each_entry(set, &mode_set_list, head) @@ -480,27 +761,8 @@ static void show_loader_logo(struct drm_device *drm_dev) */ list_for_each_entry_safe(set, tmp, &mode_set_list, head) { - struct drm_crtc *crtc = set->crtc; - list_del(&set->head); kfree(set); - - /* FIXME: - * primary plane state rotation is not BIT(0), but we only want - * it effect on logo display, userspace may not known to clean - * this property, would get unexpect display, so force set - * primary rotation to BIT(0). - */ - if (!crtc->primary || !crtc->primary->state) - continue; - - /** - * todo - * drm_atomic_plane_set_property(crtc->primary, - * crtc->primary->state, - * mode_config->rotation_property, - * BIT(0)); - */ } /* @@ -508,12 +770,21 @@ static void show_loader_logo(struct drm_device *drm_dev) */ WARN_ON(ret == -EDEADLK); - if (ret) + if (ret) { + /* + * restore display status if atomic commit failed. + */ + WARN_ON(drm_atomic_helper_swap_state(old_state, false)); goto err_free_state; + } + + rockchip_free_loader_memory(drm_dev); + drm_atomic_state_put(old_state); drm_modeset_unlock_all(drm_dev); return; - +err_free_old_state: + drm_atomic_state_put(old_state); err_free_state: drm_atomic_state_put(state); err_unlock: diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 36e62842e4bf..e63ef97ca757 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -42,6 +42,17 @@ struct rockchip_crtc_state { #define to_rockchip_crtc_state(s) \ container_of(s, struct rockchip_crtc_state, base) +struct rockchip_logo { + struct sg_table *sgt; + struct drm_mm_node mm; + dma_addr_t dma_addr; + void *kvaddr; + phys_addr_t start; + phys_addr_t size; + size_t iommu_map_size; + int count; +}; + /* * Rockchip drm private structure. * @@ -50,6 +61,7 @@ struct rockchip_crtc_state { * @mm_lock: protect drm_mm on multi-threads. */ struct rockchip_drm_private { + struct rockchip_logo *logo; struct drm_fb_helper *fbdev_helper; struct drm_gem_object *fbdev_bo; struct drm_atomic_state *state; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 14814f888c55..ead96f66bb8c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -27,15 +27,22 @@ #include "rockchip_drm_psr.h" #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) + struct rockchip_drm_fb { struct drm_framebuffer fb; dma_addr_t dma_addr[ROCKCHIP_MAX_FB_BUFFER]; + void *kvaddr[ROCKCHIP_MAX_FB_BUFFER]; struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; - struct sg_table *sgt; - phys_addr_t start; - phys_addr_t size; + struct rockchip_logo *logo; }; +bool rockchip_fb_is_logo(struct drm_framebuffer *fb) +{ + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); + + return rk_fb && rk_fb->logo; +} + dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb, unsigned int plane) { @@ -47,6 +54,16 @@ dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb, return rk_fb->dma_addr[plane]; } +void *rockchip_fb_get_kvaddr(struct drm_framebuffer *fb, unsigned int plane) +{ + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); + + if (WARN_ON(plane >= ROCKCHIP_MAX_FB_BUFFER)) + return 0; + + return rk_fb->kvaddr[plane]; +} + static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) { struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); @@ -73,11 +90,13 @@ static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { struct drm_framebuffer * rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **obj, struct resource *res, + struct drm_gem_object **obj, struct rockchip_logo *logo, unsigned int num_planes) { struct rockchip_drm_fb *rockchip_fb; struct rockchip_gem_object *rk_obj; + struct rockchip_drm_private *private = dev->dev_private; + struct drm_fb_helper *fb_helper = private->fbdev_helper; int ret = 0; int i; @@ -102,9 +121,16 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm for (i = 0; i < num_planes; i++) { rk_obj = to_rockchip_obj(obj[i]); rockchip_fb->dma_addr[i] = rk_obj->dma_addr; + rockchip_fb->kvaddr[i] = rk_obj->kvaddr; + private->fbdev_bo = &rk_obj->base; + if (fb_helper && fb_helper->fbdev && rk_obj->kvaddr) + fb_helper->fbdev->screen_base = rk_obj->kvaddr; } - } else if (res) { - /* todo for kernel logo */ + } else if (logo) { + rockchip_fb->dma_addr[0] = logo->dma_addr; + rockchip_fb->kvaddr[0] = logo->kvaddr; + rockchip_fb->logo = logo; + logo->count++; } else { ret = -EINVAL; dev_err(dev->dev, "Failed to find available buffer\n"); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h index b8d39954c565..86a401d0a545 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h @@ -15,6 +15,7 @@ #ifndef _ROCKCHIP_DRM_FB_H #define _ROCKCHIP_DRM_FB_H +bool rockchip_fb_is_logo(struct drm_framebuffer *fb); struct drm_framebuffer * rockchip_drm_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, @@ -25,9 +26,10 @@ void rockchip_drm_mode_config_init(struct drm_device *dev); struct drm_framebuffer * rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **obj, struct resource *res, + struct drm_gem_object **obj, struct rockchip_logo *logo, unsigned int num_planes); dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb, unsigned int plane); +void *rockchip_fb_get_kvaddr(struct drm_framebuffer *fb, unsigned int plane); #endif /* _ROCKCHIP_DRM_FB_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index b547c18c4924..37f6ae4d5758 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -101,6 +101,7 @@ #define to_vop(x) container_of(x, struct vop, crtc) #define to_vop_win(x) container_of(x, struct vop_win, base) +#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base) /* * The coefficients of the following matrix are all fixed points. @@ -124,7 +125,29 @@ struct vop_zpos { }; struct vop_plane_state { + struct drm_plane_state base; + int format; int zpos; + unsigned int logo_ymirror; + struct drm_rect src; + struct drm_rect dest; + dma_addr_t yrgb_mst; + dma_addr_t uv_mst; + void *yrgb_kvaddr; + const uint32_t *y2r_table; + const uint32_t *r2r_table; + const uint32_t *r2y_table; + int eotf; + bool y2r_en; + bool r2r_en; + bool r2y_en; + int color_space; + unsigned int csc_mode; + bool enable; + int global_alpha; + int blend_mode; + unsigned long offset; + int pdaf_data_type; }; struct vop_win { @@ -703,11 +726,19 @@ static int vop_plane_atomic_check(struct drm_plane *plane, struct drm_crtc_state *crtc_state; struct drm_framebuffer *fb = state->fb; struct vop_win *win = to_vop_win(plane); + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); + const struct vop_data *vop_data; + struct vop *vop; int ret; + struct drm_rect *dest = &vop_plane_state->dest; + struct drm_rect *src = &vop_plane_state->src; int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : DRM_PLANE_HELPER_NO_SCALING; int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : DRM_PLANE_HELPER_NO_SCALING; + unsigned long offset; + dma_addr_t dma_addr; + void *kvaddr; if (!crtc || !fb) return 0; @@ -716,6 +747,15 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (WARN_ON(!crtc_state)) return -EINVAL; + src->x1 = state->src_x; + src->y1 = state->src_y; + src->x2 = state->src_x + state->src_w; + src->y2 = state->src_y + state->src_h; + dest->x1 = state->crtc_x; + dest->y1 = state->crtc_y; + dest->x2 = state->crtc_x + state->crtc_w; + dest->y2 = state->crtc_y + state->crtc_h; + ret = drm_atomic_helper_check_plane_state(state, crtc_state, min_scale, max_scale, true, true); @@ -725,9 +765,22 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (!state->visible) return 0; - ret = vop_convert_format(fb->format->format); - if (ret < 0) - return ret; + vop_plane_state->format = vop_convert_format(fb->format->format); + if (vop_plane_state->format < 0) + return vop_plane_state->format; + + vop = to_vop(crtc); + vop_data = vop->data; + + if (drm_rect_width(src) >> 16 > vop_data->max_input.width || + drm_rect_height(src) >> 16 > vop_data->max_input.height) { + DRM_ERROR("Invalid source: %dx%d. max input: %dx%d\n", + drm_rect_width(src) >> 16, + drm_rect_height(src) >> 16, + vop_data->max_input.width, + vop_data->max_input.height); + return -EINVAL; + } /* * Src.x1 can be odd when do clip, but yuv plane start point @@ -743,6 +796,30 @@ static int vop_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } + offset = (src->x1 >> 16) * fb->format->cpp[0]; + vop_plane_state->offset = offset + fb->offsets[0]; + if (state->rotation & DRM_MODE_REFLECT_Y || + (rockchip_fb_is_logo(fb) && vop_plane_state->logo_ymirror)) + offset += ((src->y2 >> 16) - 1) * fb->pitches[0]; + else + offset += (src->y1 >> 16) * fb->pitches[0]; + + dma_addr = rockchip_fb_get_dma_addr(fb, 0); + kvaddr = rockchip_fb_get_kvaddr(fb, 0); + vop_plane_state->yrgb_mst = dma_addr + offset + fb->offsets[0]; + vop_plane_state->yrgb_kvaddr = kvaddr + offset + fb->offsets[0]; + if (fb->format->is_yuv) { + int hsub = drm_format_horz_chroma_subsampling(fb->format->format); + int vsub = drm_format_vert_chroma_subsampling(fb->format->format); + + offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub; + offset += (src->y1 >> 16) * fb->pitches[1] / vsub; + + dma_addr = rockchip_fb_get_dma_addr(fb, 1); + dma_addr += offset + fb->offsets[1]; + vop_plane_state->uv_mst = dma_addr; + } + return 0; } @@ -768,20 +845,18 @@ static void vop_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *state = plane->state; struct drm_crtc *crtc = state->crtc; const struct vop_win *win = to_vop_win(plane); + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); const struct vop_win_yuv2yuv_data *win_yuv2yuv = win->yuv2yuv_data; struct vop *vop = to_vop(state->crtc); struct drm_framebuffer *fb = state->fb; unsigned int actual_w, actual_h; unsigned int dsp_stx, dsp_sty; uint32_t act_info, dsp_info, dsp_st; - struct drm_rect *src = &state->src; - struct drm_rect *dest = &state->dst; - unsigned long offset; - dma_addr_t dma_addr; + struct drm_rect *src = &vop_plane_state->src; + struct drm_rect *dest = &vop_plane_state->dest; uint32_t val; bool rb_swap; int win_index = VOP_WIN_TO_INDEX(win); - int format; int is_yuv = fb->format->is_yuv; int i; @@ -810,25 +885,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane, dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start; dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff); - offset = (src->x1 >> 16) * fb->format->cpp[0]; - offset += (src->y1 >> 16) * fb->pitches[0]; - dma_addr = rockchip_fb_get_dma_addr(fb, 0); - dma_addr += offset + fb->offsets[0]; - - /* - * For y-mirroring we need to move address - * to the beginning of the last line. - */ - if (state->rotation & DRM_MODE_REFLECT_Y) - dma_addr += (actual_h - 1) * fb->pitches[0]; - - format = vop_convert_format(fb->format->format); - spin_lock(&vop->reg_lock); - VOP_WIN_SET(vop, win, format, format); + VOP_WIN_SET(vop, win, format, vop_plane_state->format); VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); - VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); + VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst); VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); VOP_WIN_SET(vop, win, y_mir_en, (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0); @@ -836,18 +897,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane, (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); if (is_yuv) { - int hsub = drm_format_horz_chroma_subsampling(fb->format->format); - int vsub = drm_format_vert_chroma_subsampling(fb->format->format); - int bpp = fb->format->cpp[1]; - - offset = (src->x1 >> 16) * bpp / hsub; - offset += (src->y1 >> 16) * fb->pitches[1] / vsub; - - dma_addr = rockchip_fb_get_dma_addr(fb, 1); - dma_addr += offset + fb->offsets[1]; - VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); - VOP_WIN_SET(vop, win, uv_mst, dma_addr); + VOP_WIN_SET(vop, win, uv_mst, vop_plane_state->yrgb_mst); for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) { VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, @@ -906,10 +957,51 @@ static void vop_plane_destroy(struct drm_plane *plane) static void vop_plane_reset(struct drm_plane *plane) { + struct vop_plane_state *vop_plane_state = + to_vop_plane_state(plane->state); struct vop_win *win = to_vop_win(plane); drm_atomic_helper_plane_reset(plane); + kfree(vop_plane_state); + vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL); + if (!vop_plane_state) + return; + win->state.zpos = win->win_id; + vop_plane_state->global_alpha = 0xff; + plane->state = &vop_plane_state->base; + plane->state->plane = plane; +} + +static struct drm_plane_state * +vop_atomic_plane_duplicate_state(struct drm_plane *plane) +{ + struct vop_plane_state *old_vop_plane_state; + struct vop_plane_state *vop_plane_state; + + if (WARN_ON(!plane->state)) + return NULL; + + old_vop_plane_state = to_vop_plane_state(plane->state); + vop_plane_state = kmemdup(old_vop_plane_state, + sizeof(*vop_plane_state), GFP_KERNEL); + if (!vop_plane_state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, + &vop_plane_state->base); + + return &vop_plane_state->base; +} + +static void vop_atomic_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vop_plane_state *vop_state = to_vop_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + + kfree(vop_state); } static int vop_atomic_plane_set_property(struct drm_plane *plane, @@ -951,8 +1043,8 @@ static const struct drm_plane_funcs vop_plane_funcs = { .disable_plane = drm_atomic_helper_disable_plane, .destroy = vop_plane_destroy, .reset = vop_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = vop_atomic_plane_duplicate_state, + .atomic_destroy_state = vop_atomic_plane_destroy_state, .atomic_set_property = vop_atomic_plane_set_property, .atomic_get_property = vop_atomic_plane_get_property, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 168af4de1dd7..52e9494084c8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -169,6 +169,11 @@ struct vop_win_data { unsigned int area_size; }; +struct vop_rect { + int width; + int height; +}; + struct vop_data { uint32_t version; const struct vop_intr *intr; @@ -180,6 +185,9 @@ struct vop_data { const struct vop_win_data *win; unsigned int win_size; + struct vop_rect max_input; + struct vop_rect max_output; + #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) u64 feature; }; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 2c13a5509a50..9d288f9fcdd4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -142,6 +142,8 @@ static const struct vop_common rk3036_common = { }; static const struct vop_data rk3036_vop = { + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, .intr = &rk3036_intr, .common = &rk3036_common, .modeset = &rk3036_modeset, @@ -170,6 +172,8 @@ static const struct vop_win_data rk3126_vop_win_data[] = { }; static const struct vop_data rk3126_vop = { + .max_input = {1920, 8192}, + .max_output = {1920, 1080}, .intr = &rk3036_intr, .common = &rk3036_common, .modeset = &rk3036_modeset, @@ -344,6 +348,8 @@ static const struct vop_intr rk3288_vop_intr = { static const struct vop_data rk3288_vop = { .version = VOP_VERSION(3, 1), + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .feature = VOP_FEATURE_OUTPUT_RGB10, .intr = &rk3288_vop_intr, .common = &rk3288_common, @@ -457,6 +463,8 @@ static const struct vop_misc rk3368_misc = { static const struct vop_data rk3368_vop = { .version = VOP_VERSION(3, 2), + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3368_vop_intr, .common = &rk3288_common, .modeset = &rk3288_modeset, @@ -478,6 +486,8 @@ static const struct vop_intr rk3366_vop_intr = { static const struct vop_data rk3366_vop = { .version = VOP_VERSION(3, 4), + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, .common = &rk3288_common, .modeset = &rk3288_modeset, @@ -596,6 +606,8 @@ static const struct vop_win_data rk3399_vop_win_data[] = { static const struct vop_data rk3399_vop_big = { .version = VOP_VERSION(3, 5), .feature = VOP_FEATURE_OUTPUT_RGB10, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, .common = &rk3288_common, .modeset = &rk3288_modeset, @@ -625,6 +637,8 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_lit_win_yuv2yuv_data[] = { static const struct vop_data rk3399_vop_lit = { .version = VOP_VERSION(3, 6), + .max_input = {4096, 8192}, + .max_output = {2560, 1600}, .intr = &rk3366_vop_intr, .common = &rk3288_common, .modeset = &rk3288_modeset, @@ -645,6 +659,8 @@ static const struct vop_win_data rk3228_vop_win_data[] = { static const struct vop_data rk3228_vop = { .version = VOP_VERSION(3, 7), .feature = VOP_FEATURE_OUTPUT_RGB10, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3366_vop_intr, .common = &rk3288_common, .modeset = &rk3288_modeset, @@ -711,6 +727,8 @@ static const struct vop_win_data rk3328_vop_win_data[] = { static const struct vop_data rk3328_vop = { .version = VOP_VERSION(3, 8), .feature = VOP_FEATURE_OUTPUT_RGB10, + .max_input = {4096, 8192}, + .max_output = {4096, 2160}, .intr = &rk3328_vop_intr, .common = &rk3328_common, .modeset = &rk3328_modeset, diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 4f597c0730b4..30a7beca3e4e 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -98,7 +98,8 @@ struct drm_encoder_funcs { struct drm_encoder { struct drm_device *dev; struct list_head head; - + /* @port: encoder device node */ + struct device_node *port; struct drm_mode_object base; char *name; /**