18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ 48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 88c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "tidss_crtc.h" 158c2ecf20Sopenharmony_ci#include "tidss_dispc.h" 168c2ecf20Sopenharmony_ci#include "tidss_drv.h" 178c2ecf20Sopenharmony_ci#include "tidss_plane.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* drm_plane_helper_funcs */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int tidss_plane_atomic_check(struct drm_plane *plane, 228c2ecf20Sopenharmony_ci struct drm_plane_state *state) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct drm_device *ddev = plane->dev; 258c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 268c2ecf20Sopenharmony_ci struct tidss_plane *tplane = to_tidss_plane(plane); 278c2ecf20Sopenharmony_ci const struct drm_format_info *finfo; 288c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 298c2ecf20Sopenharmony_ci u32 hw_plane = tplane->hw_plane_id; 308c2ecf20Sopenharmony_ci u32 hw_videoport; 318c2ecf20Sopenharmony_ci int ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (!state->crtc) { 368c2ecf20Sopenharmony_ci /* 378c2ecf20Sopenharmony_ci * The visible field is not reset by the DRM core but only 388c2ecf20Sopenharmony_ci * updated by drm_plane_helper_check_state(), set it manually. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci state->visible = false; 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 458c2ecf20Sopenharmony_ci if (IS_ERR(crtc_state)) 468c2ecf20Sopenharmony_ci return PTR_ERR(crtc_state); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 0, 498c2ecf20Sopenharmony_ci INT_MAX, true, true); 508c2ecf20Sopenharmony_ci if (ret < 0) 518c2ecf20Sopenharmony_ci return ret; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * The HW is only able to start drawing at subpixel boundary 558c2ecf20Sopenharmony_ci * (the two first checks bellow). At the end of a row the HW 568c2ecf20Sopenharmony_ci * can only jump integer number of subpixels forward to the 578c2ecf20Sopenharmony_ci * beginning of the next row. So we can only show picture with 588c2ecf20Sopenharmony_ci * integer subpixel width (the third check). However, after 598c2ecf20Sopenharmony_ci * reaching the end of the drawn picture the drawing starts 608c2ecf20Sopenharmony_ci * again at the absolute memory address where top left corner 618c2ecf20Sopenharmony_ci * position of the drawn picture is (so there is no need to 628c2ecf20Sopenharmony_ci * check for odd height). 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci finfo = drm_format_info(state->fb->format->format); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if ((state->src_x >> 16) % finfo->hsub != 0) { 688c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, 698c2ecf20Sopenharmony_ci "%s: x-position %u not divisible subpixel size %u\n", 708c2ecf20Sopenharmony_ci __func__, (state->src_x >> 16), finfo->hsub); 718c2ecf20Sopenharmony_ci return -EINVAL; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if ((state->src_y >> 16) % finfo->vsub != 0) { 758c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, 768c2ecf20Sopenharmony_ci "%s: y-position %u not divisible subpixel size %u\n", 778c2ecf20Sopenharmony_ci __func__, (state->src_y >> 16), finfo->vsub); 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if ((state->src_w >> 16) % finfo->hsub != 0) { 828c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, 838c2ecf20Sopenharmony_ci "%s: src width %u not divisible by subpixel size %u\n", 848c2ecf20Sopenharmony_ci __func__, (state->src_w >> 16), finfo->hsub); 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!state->visible) 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci hw_videoport = to_tidss_crtc(state->crtc)->hw_videoport; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = dispc_plane_check(tidss->dispc, hw_plane, state, hw_videoport); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void tidss_plane_atomic_update(struct drm_plane *plane, 1018c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct drm_device *ddev = plane->dev; 1048c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 1058c2ecf20Sopenharmony_ci struct tidss_plane *tplane = to_tidss_plane(plane); 1068c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 1078c2ecf20Sopenharmony_ci u32 hw_videoport; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!state->visible) { 1138c2ecf20Sopenharmony_ci dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci hw_videoport = to_tidss_crtc(state->crtc)->hw_videoport; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = dispc_plane_setup(tidss->dispc, tplane->hw_plane_id, 1208c2ecf20Sopenharmony_ci state, hw_videoport); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (ret) { 1238c2ecf20Sopenharmony_ci dev_err(plane->dev->dev, "%s: Failed to setup plane %d\n", 1248c2ecf20Sopenharmony_ci __func__, tplane->hw_plane_id); 1258c2ecf20Sopenharmony_ci dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); 1268c2ecf20Sopenharmony_ci return; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, true); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void tidss_plane_atomic_disable(struct drm_plane *plane, 1338c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct drm_device *ddev = plane->dev; 1368c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 1378c2ecf20Sopenharmony_ci struct tidss_plane *tplane = to_tidss_plane(plane); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void drm_plane_destroy(struct drm_plane *plane) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct tidss_plane *tplane = to_tidss_plane(plane); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci drm_plane_cleanup(plane); 1498c2ecf20Sopenharmony_ci kfree(tplane); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs tidss_plane_helper_funcs = { 1538c2ecf20Sopenharmony_ci .atomic_check = tidss_plane_atomic_check, 1548c2ecf20Sopenharmony_ci .atomic_update = tidss_plane_atomic_update, 1558c2ecf20Sopenharmony_ci .atomic_disable = tidss_plane_atomic_disable, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs tidss_plane_funcs = { 1598c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 1608c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 1618c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 1628c2ecf20Sopenharmony_ci .destroy = drm_plane_destroy, 1638c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 1648c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct tidss_plane *tidss_plane_create(struct tidss_device *tidss, 1688c2ecf20Sopenharmony_ci u32 hw_plane_id, u32 plane_type, 1698c2ecf20Sopenharmony_ci u32 crtc_mask, const u32 *formats, 1708c2ecf20Sopenharmony_ci u32 num_formats) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct tidss_plane *tplane; 1738c2ecf20Sopenharmony_ci enum drm_plane_type type; 1748c2ecf20Sopenharmony_ci u32 possible_crtcs; 1758c2ecf20Sopenharmony_ci u32 num_planes = tidss->feat->num_planes; 1768c2ecf20Sopenharmony_ci u32 color_encodings = (BIT(DRM_COLOR_YCBCR_BT601) | 1778c2ecf20Sopenharmony_ci BIT(DRM_COLOR_YCBCR_BT709)); 1788c2ecf20Sopenharmony_ci u32 color_ranges = (BIT(DRM_COLOR_YCBCR_FULL_RANGE) | 1798c2ecf20Sopenharmony_ci BIT(DRM_COLOR_YCBCR_LIMITED_RANGE)); 1808c2ecf20Sopenharmony_ci u32 default_encoding = DRM_COLOR_YCBCR_BT601; 1818c2ecf20Sopenharmony_ci u32 default_range = DRM_COLOR_YCBCR_FULL_RANGE; 1828c2ecf20Sopenharmony_ci u32 blend_modes = (BIT(DRM_MODE_BLEND_PREMULTI) | 1838c2ecf20Sopenharmony_ci BIT(DRM_MODE_BLEND_COVERAGE)); 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci tplane = kzalloc(sizeof(*tplane), GFP_KERNEL); 1878c2ecf20Sopenharmony_ci if (!tplane) 1888c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci tplane->hw_plane_id = hw_plane_id; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci possible_crtcs = crtc_mask; 1938c2ecf20Sopenharmony_ci type = plane_type; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(&tidss->ddev, &tplane->plane, 1968c2ecf20Sopenharmony_ci possible_crtcs, 1978c2ecf20Sopenharmony_ci &tidss_plane_funcs, 1988c2ecf20Sopenharmony_ci formats, num_formats, 1998c2ecf20Sopenharmony_ci NULL, type, NULL); 2008c2ecf20Sopenharmony_ci if (ret < 0) 2018c2ecf20Sopenharmony_ci goto err; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci drm_plane_helper_add(&tplane->plane, &tidss_plane_helper_funcs); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci drm_plane_create_zpos_property(&tplane->plane, hw_plane_id, 0, 2068c2ecf20Sopenharmony_ci num_planes - 1); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = drm_plane_create_color_properties(&tplane->plane, 2098c2ecf20Sopenharmony_ci color_encodings, 2108c2ecf20Sopenharmony_ci color_ranges, 2118c2ecf20Sopenharmony_ci default_encoding, 2128c2ecf20Sopenharmony_ci default_range); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto err; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = drm_plane_create_alpha_property(&tplane->plane); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci goto err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = drm_plane_create_blend_mode_property(&tplane->plane, blend_modes); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci goto err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return tplane; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cierr: 2278c2ecf20Sopenharmony_ci kfree(tplane); 2288c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2298c2ecf20Sopenharmony_ci} 230