1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/export.h> 4 #include <linux/slab.h> 5 6 #include <drm/drm_atomic.h> 7 #include <drm/drm_atomic_helper.h> 8 #include <drm/drm_atomic_state_helper.h> 9 #include <drm/drm_damage_helper.h> 10 #include <drm/drm_drv.h> 11 #include <drm/drm_edid.h> 12 #include <drm/drm_fourcc.h> 13 #include <drm/drm_framebuffer.h> 14 #include <drm/drm_gem_atomic_helper.h> 15 #include <drm/drm_gem_framebuffer_helper.h> 16 #include <drm/drm_panic.h> 17 #include <drm/drm_print.h> 18 #include <drm/drm_probe_helper.h> 19 20 #include "drm_sysfb_helper.h" 21 22 struct drm_display_mode drm_sysfb_mode(unsigned int width, 23 unsigned int height, 24 unsigned int width_mm, 25 unsigned int height_mm) 26 { 27 /* 28 * Assume a monitor resolution of 96 dpi to 29 * get a somewhat reasonable screen size. 30 */ 31 if (!width_mm) 32 width_mm = DRM_MODE_RES_MM(width, 96ul); 33 if (!height_mm) 34 height_mm = DRM_MODE_RES_MM(height, 96ul); 35 36 { 37 const struct drm_display_mode mode = { 38 DRM_MODE_INIT(60, width, height, width_mm, height_mm) 39 }; 40 41 return mode; 42 } 43 } 44 EXPORT_SYMBOL(drm_sysfb_mode); 45 46 /* 47 * Plane 48 */ 49 50 int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, 51 struct drm_atomic_state *new_state) 52 { 53 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 54 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); 55 struct drm_shadow_plane_state *new_shadow_plane_state = 56 to_drm_shadow_plane_state(new_plane_state); 57 struct drm_framebuffer *new_fb = new_plane_state->fb; 58 struct drm_crtc *new_crtc = new_plane_state->crtc; 59 struct drm_crtc_state *new_crtc_state = NULL; 60 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 61 int ret; 62 63 if (new_crtc) 64 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 65 66 ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 67 DRM_PLANE_NO_SCALING, 68 DRM_PLANE_NO_SCALING, 69 false, false); 70 if (ret) 71 return ret; 72 else if (!new_plane_state->visible) 73 return 0; 74 75 if (new_fb->format != sysfb->fb_format) { 76 void *buf; 77 78 /* format conversion necessary; reserve buffer */ 79 buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, 80 sysfb->fb_pitch, GFP_KERNEL); 81 if (!buf) 82 return -ENOMEM; 83 } 84 85 new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); 86 87 new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state); 88 new_sysfb_crtc_state->format = new_fb->format; 89 90 return 0; 91 } 92 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check); 93 94 void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) 95 { 96 struct drm_device *dev = plane->dev; 97 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 98 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 99 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 100 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 101 struct drm_framebuffer *fb = plane_state->fb; 102 unsigned int dst_pitch = sysfb->fb_pitch; 103 const struct drm_format_info *dst_format = sysfb->fb_format; 104 struct drm_atomic_helper_damage_iter iter; 105 struct drm_rect damage; 106 int ret, idx; 107 108 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 109 if (ret) 110 return; 111 112 if (!drm_dev_enter(dev, &idx)) 113 goto out_drm_gem_fb_end_cpu_access; 114 115 drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 116 drm_atomic_for_each_plane_damage(&iter, &damage) { 117 struct iosys_map dst = sysfb->fb_addr; 118 struct drm_rect dst_clip = plane_state->dst; 119 120 if (!drm_rect_intersect(&dst_clip, &damage)) 121 continue; 122 123 iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); 124 drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, 125 &damage, &shadow_plane_state->fmtcnv_state); 126 } 127 128 drm_dev_exit(idx); 129 out_drm_gem_fb_end_cpu_access: 130 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 131 } 132 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update); 133 134 void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane, 135 struct drm_atomic_state *state) 136 { 137 struct drm_device *dev = plane->dev; 138 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 139 struct iosys_map dst = sysfb->fb_addr; 140 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 141 void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ 142 unsigned int dst_pitch = sysfb->fb_pitch; 143 const struct drm_format_info *dst_format = sysfb->fb_format; 144 struct drm_rect dst_clip; 145 unsigned long lines, linepixels, i; 146 int idx; 147 148 drm_rect_init(&dst_clip, 149 plane_state->src_x >> 16, plane_state->src_y >> 16, 150 plane_state->src_w >> 16, plane_state->src_h >> 16); 151 152 lines = drm_rect_height(&dst_clip); 153 linepixels = drm_rect_width(&dst_clip); 154 155 if (!drm_dev_enter(dev, &idx)) 156 return; 157 158 /* Clear buffer to black if disabled */ 159 dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip); 160 for (i = 0; i < lines; ++i) { 161 memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); 162 dst_vmap += dst_pitch; 163 } 164 165 drm_dev_exit(idx); 166 } 167 EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable); 168 169 int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, 170 struct drm_scanout_buffer *sb) 171 { 172 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev); 173 174 sb->width = sysfb->fb_mode.hdisplay; 175 sb->height = sysfb->fb_mode.vdisplay; 176 sb->format = sysfb->fb_format; 177 sb->pitch[0] = sysfb->fb_pitch; 178 sb->map[0] = sysfb->fb_addr; 179 180 return 0; 181 } 182 EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); 183 184 /* 185 * CRTC 186 */ 187 188 static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state) 189 { 190 __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base); 191 192 kfree(sysfb_crtc_state); 193 } 194 195 enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc, 196 const struct drm_display_mode *mode) 197 { 198 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev); 199 200 return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode); 201 } 202 EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid); 203 204 int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) 205 { 206 struct drm_device *dev = crtc->dev; 207 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); 208 struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); 209 int ret; 210 211 if (!new_crtc_state->enable) 212 return 0; 213 214 ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 215 if (ret) 216 return ret; 217 218 if (new_crtc_state->color_mgmt_changed) { 219 const size_t gamma_lut_length = 220 sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut); 221 const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; 222 223 if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { 224 drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length); 225 return -EINVAL; 226 } 227 } 228 229 return 0; 230 } 231 EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check); 232 233 void drm_sysfb_crtc_reset(struct drm_crtc *crtc) 234 { 235 struct drm_sysfb_crtc_state *sysfb_crtc_state; 236 237 if (crtc->state) 238 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state)); 239 240 sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL); 241 if (sysfb_crtc_state) 242 __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base); 243 else 244 __drm_atomic_helper_crtc_reset(crtc, NULL); 245 } 246 EXPORT_SYMBOL(drm_sysfb_crtc_reset); 247 248 struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc) 249 { 250 struct drm_device *dev = crtc->dev; 251 struct drm_crtc_state *crtc_state = crtc->state; 252 struct drm_sysfb_crtc_state *new_sysfb_crtc_state; 253 struct drm_sysfb_crtc_state *sysfb_crtc_state; 254 255 if (drm_WARN_ON(dev, !crtc_state)) 256 return NULL; 257 258 new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL); 259 if (!new_sysfb_crtc_state) 260 return NULL; 261 262 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 263 264 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base); 265 new_sysfb_crtc_state->format = sysfb_crtc_state->format; 266 267 return &new_sysfb_crtc_state->base; 268 } 269 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state); 270 271 void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) 272 { 273 drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state)); 274 } 275 EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state); 276 277 /* 278 * Connector 279 */ 280 281 static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 282 { 283 struct drm_sysfb_device *sysfb = data; 284 const u8 *edid = sysfb->edid; 285 size_t off = block * EDID_LENGTH; 286 size_t end = off + len; 287 288 if (!edid) 289 return -EINVAL; 290 if (end > EDID_LENGTH) 291 return -EINVAL; 292 memcpy(buf, &edid[off], len); 293 294 /* 295 * We don't have EDID extensions available and reporting them 296 * will upset DRM helpers. Thus clear the extension field and 297 * update the checksum. Adding the extension flag to the checksum 298 * does this. 299 */ 300 buf[127] += buf[126]; 301 buf[126] = 0; 302 303 return 0; 304 } 305 306 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 307 { 308 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 309 const struct drm_edid *drm_edid; 310 311 if (sysfb->edid) { 312 drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 313 drm_edid_connector_update(connector, drm_edid); 314 drm_edid_free(drm_edid); 315 } 316 317 /* Return the fixed mode even with EDID */ 318 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 319 } 320 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes); 321