1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me> 4 */ 5 6 #include <linux/regmap.h> 7 8 #include <drm/drm_atomic.h> 9 #include <drm/drm_atomic_helper.h> 10 #include <drm/drm_crtc.h> 11 #include <drm/drm_fourcc.h> 12 #include <drm/drm_framebuffer.h> 13 #include <drm/drm_gem_atomic_helper.h> 14 #include <drm/drm_modeset_helper_vtables.h> 15 #include <drm/drm_plane.h> 16 #include <drm/drm_print.h> 17 18 #include "vs_crtc.h" 19 #include "vs_plane.h" 20 #include "vs_dc.h" 21 #include "vs_primary_plane_regs.h" 22 23 static int vs_primary_plane_atomic_check(struct drm_plane *plane, 24 struct drm_atomic_commit *state) 25 { 26 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 27 plane); 28 struct vs_plane_state *new_vs_plane_state = to_vs_plane_state(new_plane_state); 29 struct drm_framebuffer *fb = new_plane_state->fb; 30 struct drm_crtc *crtc = new_plane_state->crtc; 31 struct drm_crtc_state *crtc_state = NULL; 32 int ret; 33 34 if (crtc) 35 crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 36 37 ret = drm_atomic_helper_check_plane_state(new_plane_state, 38 crtc_state, 39 DRM_PLANE_NO_SCALING, 40 DRM_PLANE_NO_SCALING, 41 false, true); 42 if (ret) 43 return ret; 44 45 if (!new_plane_state->visible) 46 return 0; 47 48 ret = drm_format_to_vs_format(fb->format->format, 49 &new_vs_plane_state->format); 50 if (drm_WARN_ON_ONCE(plane->dev, ret)) 51 return ret; 52 53 return 0; 54 } 55 56 static void vs_primary_plane_commit(struct vs_dc *dc, unsigned int output) 57 { 58 regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output), 59 VSDC_FB_CONFIG_EX_COMMIT); 60 } 61 62 static void vs_primary_plane_atomic_enable(struct drm_plane *plane, 63 struct drm_atomic_commit *atomic_state) 64 { 65 struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, 66 plane); 67 struct drm_crtc *crtc = state->crtc; 68 struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 69 unsigned int output = vcrtc->id; 70 struct vs_dc *dc = vcrtc->dc; 71 72 regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output), 73 VSDC_FB_CONFIG_EX_FB_EN); 74 regmap_update_bits(dc->regs, VSDC_FB_CONFIG_EX(output), 75 VSDC_FB_CONFIG_EX_DISPLAY_ID_MASK, 76 VSDC_FB_CONFIG_EX_DISPLAY_ID(output)); 77 78 vs_primary_plane_commit(dc, output); 79 } 80 81 static void vs_primary_plane_atomic_disable(struct drm_plane *plane, 82 struct drm_atomic_commit *atomic_state) 83 { 84 struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state, 85 plane); 86 struct drm_crtc *crtc = state->crtc; 87 struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 88 unsigned int output = vcrtc->id; 89 struct vs_dc *dc = vcrtc->dc; 90 91 regmap_set_bits(dc->regs, VSDC_FB_CONFIG_EX(output), 92 VSDC_FB_CONFIG_EX_FB_EN); 93 94 vs_primary_plane_commit(dc, output); 95 } 96 97 static void vs_primary_plane_atomic_update(struct drm_plane *plane, 98 struct drm_atomic_commit *atomic_state) 99 { 100 struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, 101 plane); 102 struct vs_plane_state *vs_state = to_vs_plane_state(state); 103 struct drm_framebuffer *fb = state->fb; 104 struct drm_crtc *crtc = state->crtc; 105 struct vs_dc *dc; 106 struct vs_crtc *vcrtc; 107 unsigned int output; 108 dma_addr_t dma_addr; 109 110 if (!state->visible) { 111 vs_primary_plane_atomic_disable(plane, atomic_state); 112 return; 113 } 114 115 vcrtc = drm_crtc_to_vs_crtc(crtc); 116 output = vcrtc->id; 117 dc = vcrtc->dc; 118 119 regmap_update_bits(dc->regs, VSDC_FB_CONFIG(output), 120 VSDC_FB_CONFIG_FMT_MASK, 121 VSDC_FB_CONFIG_FMT(vs_state->format.color)); 122 regmap_update_bits(dc->regs, VSDC_FB_CONFIG(output), 123 VSDC_FB_CONFIG_SWIZZLE_MASK, 124 VSDC_FB_CONFIG_SWIZZLE(vs_state->format.swizzle)); 125 regmap_assign_bits(dc->regs, VSDC_FB_CONFIG(output), 126 VSDC_FB_CONFIG_UV_SWIZZLE_EN, 127 vs_state->format.uv_swizzle); 128 129 dma_addr = vs_fb_get_dma_addr(fb, &state->src); 130 131 regmap_write(dc->regs, VSDC_FB_ADDRESS(output), 132 lower_32_bits(dma_addr)); 133 regmap_write(dc->regs, VSDC_FB_STRIDE(output), 134 fb->pitches[0]); 135 136 regmap_write(dc->regs, VSDC_FB_TOP_LEFT(output), 137 VSDC_MAKE_PLANE_POS(state->crtc_x, state->crtc_y)); 138 regmap_write(dc->regs, VSDC_FB_BOTTOM_RIGHT(output), 139 VSDC_MAKE_PLANE_POS(state->crtc_x + state->crtc_w, 140 state->crtc_y + state->crtc_h)); 141 regmap_write(dc->regs, VSDC_FB_SIZE(output), 142 VSDC_MAKE_PLANE_SIZE(state->crtc_w, state->crtc_h)); 143 144 regmap_write(dc->regs, VSDC_FB_BLEND_CONFIG(output), 145 VSDC_FB_BLEND_CONFIG_BLEND_DISABLE); 146 147 vs_primary_plane_commit(dc, output); 148 } 149 150 static const struct drm_plane_helper_funcs vs_primary_plane_helper_funcs = { 151 .atomic_check = vs_primary_plane_atomic_check, 152 .atomic_update = vs_primary_plane_atomic_update, 153 .atomic_enable = vs_primary_plane_atomic_enable, 154 .atomic_disable = vs_primary_plane_atomic_disable, 155 }; 156 157 static const struct drm_plane_funcs vs_primary_plane_funcs = { 158 .atomic_destroy_state = vs_plane_destroy_state, 159 .atomic_duplicate_state = vs_plane_duplicate_state, 160 .disable_plane = drm_atomic_helper_disable_plane, 161 .reset = vs_plane_reset, 162 .update_plane = drm_atomic_helper_update_plane, 163 }; 164 165 struct drm_plane *vs_primary_plane_init(struct drm_device *drm_dev, struct vs_dc *dc) 166 { 167 struct drm_plane *plane; 168 169 plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0, 170 &vs_primary_plane_funcs, 171 dc->identity.formats->array, 172 dc->identity.formats->num, 173 NULL, 174 DRM_PLANE_TYPE_PRIMARY, 175 NULL); 176 177 if (IS_ERR(plane)) 178 return plane; 179 180 drm_plane_helper_add(plane, &vs_primary_plane_helper_funcs); 181 182 return plane; 183 } 184