1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <linux/iosys-map.h> 4 5 #include <drm/drm_atomic.h> 6 #include <drm/drm_atomic_helper.h> 7 #include <drm/drm_fourcc.h> 8 #include <drm/drm_gem_atomic_helper.h> 9 #include <drm/drm_gem_framebuffer_helper.h> 10 11 #include "vkms_drv.h" 12 #include "vkms_formats.h" 13 14 static const u32 vkms_formats[] = { 15 DRM_FORMAT_XRGB8888, 16 DRM_FORMAT_XRGB16161616, 17 DRM_FORMAT_RGB565 18 }; 19 20 static const u32 vkms_plane_formats[] = { 21 DRM_FORMAT_ARGB8888, 22 DRM_FORMAT_XRGB8888, 23 DRM_FORMAT_XRGB16161616, 24 DRM_FORMAT_ARGB16161616, 25 DRM_FORMAT_RGB565 26 }; 27 28 static struct drm_plane_state * 29 vkms_plane_duplicate_state(struct drm_plane *plane) 30 { 31 struct vkms_plane_state *vkms_state; 32 struct vkms_frame_info *frame_info; 33 34 vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); 35 if (!vkms_state) 36 return NULL; 37 38 frame_info = kzalloc(sizeof(*frame_info), GFP_KERNEL); 39 if (!frame_info) { 40 DRM_DEBUG_KMS("Couldn't allocate frame_info\n"); 41 kfree(vkms_state); 42 return NULL; 43 } 44 45 vkms_state->frame_info = frame_info; 46 47 __drm_gem_duplicate_shadow_plane_state(plane, &vkms_state->base); 48 49 return &vkms_state->base.base; 50 } 51 52 static void vkms_plane_destroy_state(struct drm_plane *plane, 53 struct drm_plane_state *old_state) 54 { 55 struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state); 56 struct drm_crtc *crtc = vkms_state->base.base.crtc; 57 58 if (crtc && vkms_state->frame_info->fb) { 59 /* dropping the reference we acquired in 60 * vkms_primary_plane_update() 61 */ 62 if (drm_framebuffer_read_refcount(vkms_state->frame_info->fb)) 63 drm_framebuffer_put(vkms_state->frame_info->fb); 64 } 65 66 kfree(vkms_state->frame_info); 67 vkms_state->frame_info = NULL; 68 69 __drm_gem_destroy_shadow_plane_state(&vkms_state->base); 70 kfree(vkms_state); 71 } 72 73 static void vkms_plane_reset(struct drm_plane *plane) 74 { 75 struct vkms_plane_state *vkms_state; 76 77 if (plane->state) { 78 vkms_plane_destroy_state(plane, plane->state); 79 plane->state = NULL; /* must be set to NULL here */ 80 } 81 82 vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); 83 if (!vkms_state) { 84 DRM_ERROR("Cannot allocate vkms_plane_state\n"); 85 return; 86 } 87 88 __drm_gem_reset_shadow_plane(plane, &vkms_state->base); 89 } 90 91 static const struct drm_plane_funcs vkms_plane_funcs = { 92 .update_plane = drm_atomic_helper_update_plane, 93 .disable_plane = drm_atomic_helper_disable_plane, 94 .reset = vkms_plane_reset, 95 .atomic_duplicate_state = vkms_plane_duplicate_state, 96 .atomic_destroy_state = vkms_plane_destroy_state, 97 }; 98 99 static void vkms_plane_atomic_update(struct drm_plane *plane, 100 struct drm_atomic_state *state) 101 { 102 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 103 plane); 104 struct vkms_plane_state *vkms_plane_state; 105 struct drm_shadow_plane_state *shadow_plane_state; 106 struct drm_framebuffer *fb = new_state->fb; 107 struct vkms_frame_info *frame_info; 108 u32 fmt; 109 110 if (!new_state->crtc || !fb) 111 return; 112 113 fmt = fb->format->format; 114 vkms_plane_state = to_vkms_plane_state(new_state); 115 shadow_plane_state = &vkms_plane_state->base; 116 117 frame_info = vkms_plane_state->frame_info; 118 memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect)); 119 memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect)); 120 frame_info->fb = fb; 121 memcpy(&frame_info->map, &shadow_plane_state->data, sizeof(frame_info->map)); 122 drm_framebuffer_get(frame_info->fb); 123 frame_info->offset = fb->offsets[0]; 124 frame_info->pitch = fb->pitches[0]; 125 frame_info->cpp = fb->format->cpp[0]; 126 vkms_plane_state->plane_read = get_frame_to_line_function(fmt); 127 } 128 129 static int vkms_plane_atomic_check(struct drm_plane *plane, 130 struct drm_atomic_state *state) 131 { 132 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 133 plane); 134 struct drm_crtc_state *crtc_state; 135 bool can_position = false; 136 int ret; 137 138 if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc)) 139 return 0; 140 141 crtc_state = drm_atomic_get_crtc_state(state, 142 new_plane_state->crtc); 143 if (IS_ERR(crtc_state)) 144 return PTR_ERR(crtc_state); 145 146 if (plane->type != DRM_PLANE_TYPE_PRIMARY) 147 can_position = true; 148 149 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 150 DRM_PLANE_NO_SCALING, 151 DRM_PLANE_NO_SCALING, 152 can_position, true); 153 if (ret != 0) 154 return ret; 155 156 /* for now primary plane must be visible and full screen */ 157 if (!new_plane_state->visible && !can_position) 158 return -EINVAL; 159 160 return 0; 161 } 162 163 static int vkms_prepare_fb(struct drm_plane *plane, 164 struct drm_plane_state *state) 165 { 166 struct drm_shadow_plane_state *shadow_plane_state; 167 struct drm_framebuffer *fb = state->fb; 168 int ret; 169 170 if (!fb) 171 return 0; 172 173 shadow_plane_state = to_drm_shadow_plane_state(state); 174 175 ret = drm_gem_plane_helper_prepare_fb(plane, state); 176 if (ret) 177 return ret; 178 179 return drm_gem_fb_vmap(fb, shadow_plane_state->map, shadow_plane_state->data); 180 } 181 182 static void vkms_cleanup_fb(struct drm_plane *plane, 183 struct drm_plane_state *state) 184 { 185 struct drm_shadow_plane_state *shadow_plane_state; 186 struct drm_framebuffer *fb = state->fb; 187 188 if (!fb) 189 return; 190 191 shadow_plane_state = to_drm_shadow_plane_state(state); 192 193 drm_gem_fb_vunmap(fb, shadow_plane_state->map); 194 } 195 196 static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = { 197 .atomic_update = vkms_plane_atomic_update, 198 .atomic_check = vkms_plane_atomic_check, 199 .prepare_fb = vkms_prepare_fb, 200 .cleanup_fb = vkms_cleanup_fb, 201 }; 202 203 struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev, 204 enum drm_plane_type type, int index) 205 { 206 struct drm_device *dev = &vkmsdev->drm; 207 const struct drm_plane_helper_funcs *funcs; 208 struct vkms_plane *plane; 209 const u32 *formats; 210 int nformats; 211 212 switch (type) { 213 case DRM_PLANE_TYPE_PRIMARY: 214 formats = vkms_formats; 215 nformats = ARRAY_SIZE(vkms_formats); 216 funcs = &vkms_primary_helper_funcs; 217 break; 218 case DRM_PLANE_TYPE_CURSOR: 219 case DRM_PLANE_TYPE_OVERLAY: 220 formats = vkms_plane_formats; 221 nformats = ARRAY_SIZE(vkms_plane_formats); 222 funcs = &vkms_primary_helper_funcs; 223 break; 224 default: 225 formats = vkms_formats; 226 nformats = ARRAY_SIZE(vkms_formats); 227 funcs = &vkms_primary_helper_funcs; 228 break; 229 } 230 231 plane = drmm_universal_plane_alloc(dev, struct vkms_plane, base, 1 << index, 232 &vkms_plane_funcs, 233 formats, nformats, 234 NULL, type, NULL); 235 if (IS_ERR(plane)) 236 return plane; 237 238 drm_plane_helper_add(&plane->base, funcs); 239 240 return plane; 241 } 242