379 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2015 MediaTek Inc.4 * Author: CK Hu <ck.hu@mediatek.com>5 */6 7#include <drm/drm_atomic.h>8#include <drm/drm_atomic_helper.h>9#include <drm/drm_atomic_uapi.h>10#include <drm/drm_blend.h>11#include <drm/drm_fourcc.h>12#include <drm/drm_framebuffer.h>13#include <drm/drm_gem_atomic_helper.h>14#include <linux/align.h>15 16#include "mtk_crtc.h"17#include "mtk_ddp_comp.h"18#include "mtk_drm_drv.h"19#include "mtk_gem.h"20#include "mtk_plane.h"21 22static const u64 modifiers[] = {23 DRM_FORMAT_MOD_LINEAR,24 DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |25 AFBC_FORMAT_MOD_SPLIT |26 AFBC_FORMAT_MOD_SPARSE),27 DRM_FORMAT_MOD_INVALID,28};29 30static void mtk_plane_reset(struct drm_plane *plane)31{32 struct mtk_plane_state *state;33 34 if (plane->state) {35 __drm_atomic_helper_plane_destroy_state(plane->state);36 37 state = to_mtk_plane_state(plane->state);38 memset(state, 0, sizeof(*state));39 } else {40 state = kzalloc(sizeof(*state), GFP_KERNEL);41 if (!state)42 return;43 }44 45 __drm_atomic_helper_plane_reset(plane, &state->base);46 47 state->base.plane = plane;48 state->pending.format = DRM_FORMAT_RGB565;49 state->pending.modifier = DRM_FORMAT_MOD_LINEAR;50}51 52static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)53{54 struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);55 struct mtk_plane_state *state;56 57 state = kmalloc(sizeof(*state), GFP_KERNEL);58 if (!state)59 return NULL;60 61 __drm_atomic_helper_plane_duplicate_state(plane, &state->base);62 63 WARN_ON(state->base.plane != plane);64 65 state->pending = old_state->pending;66 67 return &state->base;68}69 70static bool mtk_plane_format_mod_supported(struct drm_plane *plane,71 uint32_t format,72 uint64_t modifier)73{74 if (modifier == DRM_FORMAT_MOD_LINEAR)75 return true;76 77 if (modifier != DRM_FORMAT_MOD_ARM_AFBC(78 AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |79 AFBC_FORMAT_MOD_SPLIT |80 AFBC_FORMAT_MOD_SPARSE))81 return false;82 83 if (format != DRM_FORMAT_XRGB8888 &&84 format != DRM_FORMAT_ARGB8888 &&85 format != DRM_FORMAT_BGRX8888 &&86 format != DRM_FORMAT_BGRA8888 &&87 format != DRM_FORMAT_ABGR8888 &&88 format != DRM_FORMAT_XBGR8888 &&89 format != DRM_FORMAT_RGB888 &&90 format != DRM_FORMAT_BGR888)91 return false;92 93 return true;94}95 96static void mtk_plane_destroy_state(struct drm_plane *plane,97 struct drm_plane_state *state)98{99 __drm_atomic_helper_plane_destroy_state(state);100 kfree(to_mtk_plane_state(state));101}102 103static int mtk_plane_atomic_async_check(struct drm_plane *plane,104 struct drm_atomic_state *state)105{106 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,107 plane);108 struct drm_crtc_state *crtc_state;109 int ret;110 111 if (plane != new_plane_state->crtc->cursor)112 return -EINVAL;113 114 if (!plane->state)115 return -EINVAL;116 117 if (!plane->state->fb)118 return -EINVAL;119 120 ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,121 to_mtk_plane_state(new_plane_state));122 if (ret)123 return ret;124 125 crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc);126 127 return drm_atomic_helper_check_plane_state(plane->state, crtc_state,128 DRM_PLANE_NO_SCALING,129 DRM_PLANE_NO_SCALING,130 true, true);131}132 133static void mtk_plane_update_new_state(struct drm_plane_state *new_state,134 struct mtk_plane_state *mtk_plane_state)135{136 struct drm_framebuffer *fb = new_state->fb;137 struct drm_gem_object *gem;138 struct mtk_gem_obj *mtk_gem;139 unsigned int pitch, format;140 u64 modifier;141 dma_addr_t addr;142 dma_addr_t hdr_addr = 0;143 unsigned int hdr_pitch = 0;144 int offset;145 146 gem = fb->obj[0];147 mtk_gem = to_mtk_gem_obj(gem);148 addr = mtk_gem->dma_addr;149 pitch = fb->pitches[0];150 format = fb->format->format;151 modifier = fb->modifier;152 153 if (modifier == DRM_FORMAT_MOD_LINEAR) {154 /*155 * Using dma_addr_t variable to calculate with multiplier of different types,156 * for example: addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];157 * may cause coverity issue with unintentional overflow.158 */159 offset = (new_state->src.x1 >> 16) * fb->format->cpp[0];160 addr += offset;161 offset = (new_state->src.y1 >> 16) * pitch;162 addr += offset;163 } else {164 int width_in_blocks = ALIGN(fb->width, AFBC_DATA_BLOCK_WIDTH)165 / AFBC_DATA_BLOCK_WIDTH;166 int height_in_blocks = ALIGN(fb->height, AFBC_DATA_BLOCK_HEIGHT)167 / AFBC_DATA_BLOCK_HEIGHT;168 int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;169 int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;170 int hdr_size, hdr_offset;171 172 hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;173 pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *174 AFBC_DATA_BLOCK_HEIGHT * fb->format->cpp[0];175 176 hdr_size = ALIGN(hdr_pitch * height_in_blocks, AFBC_HEADER_ALIGNMENT);177 hdr_offset = hdr_pitch * y_offset_in_blocks +178 AFBC_HEADER_BLOCK_SIZE * x_offset_in_blocks;179 180 /*181 * Using dma_addr_t variable to calculate with multiplier of different types,182 * for example: addr += hdr_pitch * y_offset_in_blocks;183 * may cause coverity issue with unintentional overflow.184 */185 hdr_addr = addr + hdr_offset;186 187 /* The data plane is offset by 1 additional block. */188 offset = pitch * y_offset_in_blocks +189 AFBC_DATA_BLOCK_WIDTH * AFBC_DATA_BLOCK_HEIGHT *190 fb->format->cpp[0] * (x_offset_in_blocks + 1);191 192 /*193 * Using dma_addr_t variable to calculate with multiplier of different types,194 * for example: addr += pitch * y_offset_in_blocks;195 * may cause coverity issue with unintentional overflow.196 */197 addr = addr + hdr_size + offset;198 }199 200 mtk_plane_state->pending.enable = true;201 mtk_plane_state->pending.pitch = pitch;202 mtk_plane_state->pending.hdr_pitch = hdr_pitch;203 mtk_plane_state->pending.format = format;204 mtk_plane_state->pending.modifier = modifier;205 mtk_plane_state->pending.addr = addr;206 mtk_plane_state->pending.hdr_addr = hdr_addr;207 mtk_plane_state->pending.x = new_state->dst.x1;208 mtk_plane_state->pending.y = new_state->dst.y1;209 mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);210 mtk_plane_state->pending.height = drm_rect_height(&new_state->dst);211 mtk_plane_state->pending.rotation = new_state->rotation;212 mtk_plane_state->pending.color_encoding = new_state->color_encoding;213}214 215static void mtk_plane_atomic_async_update(struct drm_plane *plane,216 struct drm_atomic_state *state)217{218 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,219 plane);220 struct mtk_plane_state *new_plane_state = to_mtk_plane_state(plane->state);221 222 plane->state->crtc_x = new_state->crtc_x;223 plane->state->crtc_y = new_state->crtc_y;224 plane->state->crtc_h = new_state->crtc_h;225 plane->state->crtc_w = new_state->crtc_w;226 plane->state->src_x = new_state->src_x;227 plane->state->src_y = new_state->src_y;228 plane->state->src_h = new_state->src_h;229 plane->state->src_w = new_state->src_w;230 plane->state->dst.x1 = new_state->dst.x1;231 plane->state->dst.y1 = new_state->dst.y1;232 233 mtk_plane_update_new_state(new_state, new_plane_state);234 swap(plane->state->fb, new_state->fb);235 wmb(); /* Make sure the above parameters are set before update */236 new_plane_state->pending.async_dirty = true;237 mtk_crtc_async_update(new_state->crtc, plane, state);238}239 240static const struct drm_plane_funcs mtk_plane_funcs = {241 .update_plane = drm_atomic_helper_update_plane,242 .disable_plane = drm_atomic_helper_disable_plane,243 .destroy = drm_plane_cleanup,244 .reset = mtk_plane_reset,245 .atomic_duplicate_state = mtk_plane_duplicate_state,246 .atomic_destroy_state = mtk_plane_destroy_state,247 .format_mod_supported = mtk_plane_format_mod_supported,248};249 250static int mtk_plane_atomic_check(struct drm_plane *plane,251 struct drm_atomic_state *state)252{253 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,254 plane);255 struct drm_framebuffer *fb = new_plane_state->fb;256 struct drm_crtc_state *crtc_state;257 int ret;258 259 if (!fb)260 return 0;261 262 if (WARN_ON(!new_plane_state->crtc))263 return 0;264 265 ret = mtk_crtc_plane_check(new_plane_state->crtc, plane,266 to_mtk_plane_state(new_plane_state));267 if (ret)268 return ret;269 270 crtc_state = drm_atomic_get_crtc_state(state,271 new_plane_state->crtc);272 if (IS_ERR(crtc_state))273 return PTR_ERR(crtc_state);274 275 return drm_atomic_helper_check_plane_state(new_plane_state,276 crtc_state,277 DRM_PLANE_NO_SCALING,278 DRM_PLANE_NO_SCALING,279 true, true);280}281 282static void mtk_plane_atomic_disable(struct drm_plane *plane,283 struct drm_atomic_state *state)284{285 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,286 plane);287 struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);288 mtk_plane_state->pending.enable = false;289 wmb(); /* Make sure the above parameter is set before update */290 mtk_plane_state->pending.dirty = true;291}292 293static void mtk_plane_atomic_update(struct drm_plane *plane,294 struct drm_atomic_state *state)295{296 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,297 plane);298 struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state);299 300 if (!new_state->crtc || WARN_ON(!new_state->fb))301 return;302 303 if (!new_state->visible) {304 mtk_plane_atomic_disable(plane, state);305 return;306 }307 308 mtk_plane_update_new_state(new_state, mtk_plane_state);309 wmb(); /* Make sure the above parameters are set before update */310 mtk_plane_state->pending.dirty = true;311}312 313static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {314 .atomic_check = mtk_plane_atomic_check,315 .atomic_update = mtk_plane_atomic_update,316 .atomic_disable = mtk_plane_atomic_disable,317 .atomic_async_update = mtk_plane_atomic_async_update,318 .atomic_async_check = mtk_plane_atomic_async_check,319};320 321int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,322 unsigned long possible_crtcs, enum drm_plane_type type,323 unsigned int supported_rotations, const u32 blend_modes,324 const u32 *formats, size_t num_formats, unsigned int plane_idx)325{326 int err;327 328 if (!formats || !num_formats) {329 DRM_ERROR("no formats for plane\n");330 return -EINVAL;331 }332 333 err = drm_universal_plane_init(dev, plane, possible_crtcs,334 &mtk_plane_funcs, formats,335 num_formats, modifiers, type, NULL);336 if (err) {337 DRM_ERROR("failed to initialize plane\n");338 return err;339 }340 341 /*342 * The hardware does not support repositioning planes by muxing: their343 * Z-position is infact fixed and the only way to change the actual344 * order is to swap the contents of the entire register set of one345 * overlay with another, which may be more expensive than desired.346 *347 * With no repositioning, the caller of this function guarantees that348 * the plane_idx is correct. This means that, for example, the PRIMARY349 * plane fed to this function will always have plane_idx zero.350 */351 err = drm_plane_create_zpos_immutable_property(plane, plane_idx);352 if (err) {353 DRM_ERROR("Failed to create zpos property for plane %u\n", plane_idx);354 return err;355 }356 357 if (supported_rotations) {358 err = drm_plane_create_rotation_property(plane,359 DRM_MODE_ROTATE_0,360 supported_rotations);361 if (err)362 DRM_INFO("Create rotation property failed\n");363 }364 365 err = drm_plane_create_alpha_property(plane);366 if (err)367 DRM_ERROR("failed to create property: alpha\n");368 369 if (blend_modes) {370 err = drm_plane_create_blend_mode_property(plane, blend_modes);371 if (err)372 DRM_ERROR("failed to create property: blend_mode\n");373 }374 375 drm_plane_helper_add(plane, &mtk_plane_helper_funcs);376 377 return 0;378}379