317 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * shmob_drm_plane.c -- SH Mobile DRM Planes4 *5 * Copyright (C) 2012 Renesas Electronics Corporation6 *7 * Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <drm/drm_atomic.h>11#include <drm/drm_atomic_helper.h>12#include <drm/drm_crtc.h>13#include <drm/drm_fb_dma_helper.h>14#include <drm/drm_fourcc.h>15#include <drm/drm_framebuffer.h>16#include <drm/drm_gem_dma_helper.h>17 18#include "shmob_drm_drv.h"19#include "shmob_drm_kms.h"20#include "shmob_drm_plane.h"21#include "shmob_drm_regs.h"22 23struct shmob_drm_plane {24 struct drm_plane base;25 unsigned int index;26};27 28struct shmob_drm_plane_state {29 struct drm_plane_state base;30 31 const struct shmob_drm_format_info *format;32 u32 dma[2];33};34 35static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane)36{37 return container_of(plane, struct shmob_drm_plane, base);38}39 40static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state)41{42 return container_of(state, struct shmob_drm_plane_state, base);43}44 45static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate)46{47 struct drm_framebuffer *fb = sstate->base.fb;48 unsigned int x = sstate->base.src_x >> 16;49 unsigned int y = sstate->base.src_y >> 16;50 struct drm_gem_dma_object *gem;51 unsigned int bpp;52 53 bpp = shmob_drm_format_is_yuv(sstate->format) ? 8 : sstate->format->bpp;54 gem = drm_fb_dma_get_gem_obj(fb, 0);55 sstate->dma[0] = gem->dma_addr + fb->offsets[0]56 + y * fb->pitches[0] + x * bpp / 8;57 58 if (shmob_drm_format_is_yuv(sstate->format)) {59 bpp = sstate->format->bpp - 8;60 gem = drm_fb_dma_get_gem_obj(fb, 1);61 sstate->dma[1] = gem->dma_addr + fb->offsets[1]62 + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]63 + x * (bpp == 16 ? 2 : 1);64 }65}66 67static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,68 struct drm_plane_state *state)69{70 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);71 struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);72 struct drm_framebuffer *fb = state->fb;73 74 /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */75 lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1);76 lcdc_write(sdev, LDMLSR, fb->pitches[0]);77 78 /* Word and long word swap. */79 lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);80 81 lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);82 if (shmob_drm_format_is_yuv(sstate->format))83 lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);84 85 lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);86}87 88static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,89 struct drm_plane_state *state)90{91 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);92 struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);93 struct drm_framebuffer *fb = state->fb;94 u32 format;95 96 /* TODO: Support ROP3 mode */97 format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) |98 sstate->format->ldbbsifr;99 100#define plane_reg_dump(sdev, splane, reg) \101 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \102 splane->index, #reg, \103 lcdc_read(sdev, reg(splane->index)), \104 lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))105 106 plane_reg_dump(sdev, splane, LDBnBSIFR);107 plane_reg_dump(sdev, splane, LDBnBSSZR);108 plane_reg_dump(sdev, splane, LDBnBLOCR);109 plane_reg_dump(sdev, splane, LDBnBSMWR);110 plane_reg_dump(sdev, splane, LDBnBSAYR);111 plane_reg_dump(sdev, splane, LDBnBSACR);112 113 lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));114 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,115 "LDBCR", lcdc_read(sdev, LDBCR));116 117 lcdc_write(sdev, LDBnBSIFR(splane->index), format);118 119 lcdc_write(sdev, LDBnBSSZR(splane->index),120 (state->crtc_h << LDBBSSZR_BVSS_SHIFT) |121 (state->crtc_w << LDBBSSZR_BHSS_SHIFT));122 lcdc_write(sdev, LDBnBLOCR(splane->index),123 (state->crtc_y << LDBBLOCR_CVLC_SHIFT) |124 (state->crtc_x << LDBBLOCR_CHLC_SHIFT));125 lcdc_write(sdev, LDBnBSMWR(splane->index),126 fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);127 128 lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]);129 if (shmob_drm_format_is_yuv(sstate->format))130 lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]);131 132 lcdc_write(sdev, LDBCR,133 LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));134 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,135 "LDBCR", lcdc_read(sdev, LDBCR));136 137 plane_reg_dump(sdev, splane, LDBnBSIFR);138 plane_reg_dump(sdev, splane, LDBnBSSZR);139 plane_reg_dump(sdev, splane, LDBnBLOCR);140 plane_reg_dump(sdev, splane, LDBnBSMWR);141 plane_reg_dump(sdev, splane, LDBnBSAYR);142 plane_reg_dump(sdev, splane, LDBnBSACR);143}144 145static int shmob_drm_plane_atomic_check(struct drm_plane *plane,146 struct drm_atomic_state *state)147{148 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);149 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(new_plane_state);150 struct drm_crtc_state *crtc_state;151 bool is_primary = plane->type == DRM_PLANE_TYPE_PRIMARY;152 int ret;153 154 if (!new_plane_state->crtc) {155 /*156 * The visible field is not reset by the DRM core but only157 * updated by drm_atomic_helper_check_plane_state(), set it158 * manually.159 */160 new_plane_state->visible = false;161 sstate->format = NULL;162 return 0;163 }164 165 crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);166 if (IS_ERR(crtc_state))167 return PTR_ERR(crtc_state);168 169 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,170 DRM_PLANE_NO_SCALING,171 DRM_PLANE_NO_SCALING,172 !is_primary, true);173 if (ret < 0)174 return ret;175 176 if (!new_plane_state->visible) {177 sstate->format = NULL;178 return 0;179 }180 181 sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format);182 if (!sstate->format) {183 dev_dbg(plane->dev->dev,184 "plane_atomic_check: unsupported format %p4cc\n",185 &new_plane_state->fb->format->format);186 return -EINVAL;187 }188 189 shmob_drm_plane_compute_base(sstate);190 191 return 0;192}193 194static void shmob_drm_plane_atomic_update(struct drm_plane *plane,195 struct drm_atomic_state *state)196{197 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);198 struct shmob_drm_plane *splane = to_shmob_plane(plane);199 200 if (!new_plane_state->visible)201 return;202 203 if (plane->type == DRM_PLANE_TYPE_PRIMARY)204 shmob_drm_primary_plane_setup(splane, new_plane_state);205 else206 shmob_drm_overlay_plane_setup(splane, new_plane_state);207}208 209static void shmob_drm_plane_atomic_disable(struct drm_plane *plane,210 struct drm_atomic_state *state)211{212 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);213 struct shmob_drm_device *sdev = to_shmob_device(plane->dev);214 struct shmob_drm_plane *splane = to_shmob_plane(plane);215 216 if (!old_state->crtc)217 return;218 219 if (plane->type != DRM_PLANE_TYPE_OVERLAY)220 return;221 222 lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));223 lcdc_write(sdev, LDBnBSIFR(splane->index), 0);224 lcdc_write(sdev, LDBCR,225 LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));226}227 228static struct drm_plane_state *229shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane)230{231 struct shmob_drm_plane_state *state;232 struct shmob_drm_plane_state *copy;233 234 if (WARN_ON(!plane->state))235 return NULL;236 237 state = to_shmob_plane_state(plane->state);238 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);239 if (copy == NULL)240 return NULL;241 242 __drm_atomic_helper_plane_duplicate_state(plane, ©->base);243 244 return ©->base;245}246 247static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane,248 struct drm_plane_state *state)249{250 __drm_atomic_helper_plane_destroy_state(state);251 kfree(to_shmob_plane_state(state));252}253 254static void shmob_drm_plane_reset(struct drm_plane *plane)255{256 struct shmob_drm_plane_state *state;257 258 if (plane->state) {259 shmob_drm_plane_atomic_destroy_state(plane, plane->state);260 plane->state = NULL;261 }262 263 state = kzalloc(sizeof(*state), GFP_KERNEL);264 if (state == NULL)265 return;266 267 __drm_atomic_helper_plane_reset(plane, &state->base);268}269 270static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = {271 .atomic_check = shmob_drm_plane_atomic_check,272 .atomic_update = shmob_drm_plane_atomic_update,273 .atomic_disable = shmob_drm_plane_atomic_disable,274};275 276static const struct drm_plane_funcs shmob_drm_plane_funcs = {277 .update_plane = drm_atomic_helper_update_plane,278 .disable_plane = drm_atomic_helper_disable_plane,279 .reset = shmob_drm_plane_reset,280 .atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state,281 .atomic_destroy_state = shmob_drm_plane_atomic_destroy_state,282};283 284static const uint32_t formats[] = {285 DRM_FORMAT_RGB565,286 DRM_FORMAT_RGB888,287 DRM_FORMAT_ARGB8888,288 DRM_FORMAT_XRGB8888,289 DRM_FORMAT_NV12,290 DRM_FORMAT_NV21,291 DRM_FORMAT_NV16,292 DRM_FORMAT_NV61,293 DRM_FORMAT_NV24,294 DRM_FORMAT_NV42,295};296 297struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,298 enum drm_plane_type type,299 unsigned int index)300{301 struct shmob_drm_plane *splane;302 303 splane = drmm_universal_plane_alloc(&sdev->ddev,304 struct shmob_drm_plane, base, 1,305 &shmob_drm_plane_funcs, formats,306 ARRAY_SIZE(formats), NULL, type,307 NULL);308 if (IS_ERR(splane))309 return ERR_CAST(splane);310 311 splane->index = index;312 313 drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs);314 315 return &splane->base;316}317