1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/aperture.h> 4 #include <linux/coreboot.h> 5 #include <linux/minmax.h> 6 #include <linux/platform_device.h> 7 8 #include <drm/clients/drm_client_setup.h> 9 #include <drm/drm_atomic.h> 10 #include <drm/drm_atomic_state_helper.h> 11 #include <drm/drm_connector.h> 12 #include <drm/drm_damage_helper.h> 13 #include <drm/drm_device.h> 14 #include <drm/drm_drv.h> 15 #include <drm/drm_fbdev_shmem.h> 16 #include <drm/drm_framebuffer.h> 17 #include <drm/drm_gem_atomic_helper.h> 18 #include <drm/drm_gem_framebuffer_helper.h> 19 #include <drm/drm_gem_shmem_helper.h> 20 #include <drm/drm_managed.h> 21 #include <drm/drm_modeset_helper_vtables.h> 22 #include <drm/drm_print.h> 23 #include <drm/drm_probe_helper.h> 24 25 #include "drm_sysfb_helper.h" 26 27 #define DRIVER_NAME "corebootdrm" 28 #define DRIVER_DESC "DRM driver for Coreboot framebuffers" 29 #define DRIVER_MAJOR 1 30 #define DRIVER_MINOR 0 31 32 static const struct drm_format_info * 33 corebootdrm_get_format_fb(struct drm_device *dev, const struct lb_framebuffer *fb) 34 { 35 static const struct drm_sysfb_format formats[] = { 36 { PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, }, 37 { PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, }, 38 { PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, }, 39 { PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, }, 40 { PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, }, 41 { PIXEL_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010, }, 42 }; 43 const struct pixel_format pixel = { 44 .bits_per_pixel = fb->bits_per_pixel, 45 .indexed = false, 46 .alpha = { 47 .offset = 0, 48 .length = 0, 49 }, 50 .red = { 51 .offset = fb->red_mask_pos, 52 .length = fb->red_mask_size, 53 }, 54 .green = { 55 .offset = fb->green_mask_pos, 56 .length = fb->green_mask_size, 57 }, 58 .blue = { 59 .offset = fb->blue_mask_pos, 60 .length = fb->blue_mask_size, 61 }, 62 }; 63 64 return drm_sysfb_get_format(dev, formats, ARRAY_SIZE(formats), &pixel); 65 } 66 67 static int corebootdrm_get_width_fb(struct drm_device *dev, const struct lb_framebuffer *fb) 68 { 69 return drm_sysfb_get_validated_int0(dev, "width", fb->x_resolution, INT_MAX); 70 } 71 72 static int corebootdrm_get_height_fb(struct drm_device *dev, const struct lb_framebuffer *fb) 73 { 74 return drm_sysfb_get_validated_int0(dev, "height", fb->y_resolution, INT_MAX); 75 } 76 77 static int corebootdrm_get_pitch_fb(struct drm_device *dev, const struct drm_format_info *format, 78 unsigned int width, const struct lb_framebuffer *fb) 79 { 80 u64 bytes_per_line = fb->bytes_per_line; 81 82 if (!bytes_per_line) 83 bytes_per_line = drm_format_info_min_pitch(format, 0, width); 84 85 return drm_sysfb_get_validated_int0(dev, "pitch", bytes_per_line, INT_MAX); 86 } 87 88 static resource_size_t corebootdrm_get_size_fb(struct drm_device *dev, unsigned int height, 89 unsigned int pitch, 90 const struct lb_framebuffer *fb) 91 { 92 resource_size_t size; 93 94 if (check_mul_overflow(height, pitch, &size)) 95 return 0; 96 97 return size; 98 } 99 100 static phys_addr_t corebootdrm_get_address_fb(struct drm_device *dev, resource_size_t size, 101 const struct lb_framebuffer *fb) 102 { 103 if (size > PHYS_ADDR_MAX) 104 return 0; 105 if (!fb->physical_address) 106 return 0; 107 if (fb->physical_address > (PHYS_ADDR_MAX - size)) 108 return 0; 109 110 return fb->physical_address; 111 } 112 113 static enum drm_panel_orientation corebootdrm_get_orientation_fb(struct drm_device *dev, 114 const struct lb_framebuffer *fb) 115 { 116 if (!LB_FRAMEBUFFER_HAS_ORIENTATION(fb)) 117 return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 118 119 switch (fb->orientation) { 120 case LB_FRAMEBUFFER_ORIENTATION_NORMAL: 121 return DRM_MODE_PANEL_ORIENTATION_NORMAL; 122 case LB_FRAMEBUFFER_ORIENTATION_BOTTOM_UP: 123 return DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP; 124 case LB_FRAMEBUFFER_ORIENTATION_LEFT_UP: 125 return DRM_MODE_PANEL_ORIENTATION_LEFT_UP; 126 case LB_FRAMEBUFFER_ORIENTATION_RIGHT_UP: 127 return DRM_MODE_PANEL_ORIENTATION_RIGHT_UP; 128 } 129 130 return DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 131 } 132 133 /* 134 * Simple Framebuffer device 135 */ 136 137 struct corebootdrm_device { 138 struct drm_sysfb_device sysfb; 139 140 /* modesetting */ 141 u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)]; 142 struct drm_plane primary_plane; 143 struct drm_crtc crtc; 144 struct drm_encoder encoder; 145 struct drm_connector connector; 146 }; 147 148 /* 149 * Modesetting 150 */ 151 152 static const u64 corebootdrm_primary_plane_format_modifiers[] = { 153 DRM_SYSFB_PLANE_FORMAT_MODIFIERS, 154 }; 155 156 static const struct drm_plane_helper_funcs corebootdrm_primary_plane_helper_funcs = { 157 DRM_SYSFB_PLANE_HELPER_FUNCS, 158 }; 159 160 static const struct drm_plane_funcs corebootdrm_primary_plane_funcs = { 161 DRM_SYSFB_PLANE_FUNCS, 162 .destroy = drm_plane_cleanup, 163 }; 164 165 static const struct drm_crtc_helper_funcs corebootdrm_crtc_helper_funcs = { 166 DRM_SYSFB_CRTC_HELPER_FUNCS, 167 }; 168 169 static const struct drm_crtc_funcs corebootdrm_crtc_funcs = { 170 DRM_SYSFB_CRTC_FUNCS, 171 .destroy = drm_crtc_cleanup, 172 }; 173 174 static const struct drm_encoder_funcs corebootdrm_encoder_funcs = { 175 .destroy = drm_encoder_cleanup, 176 }; 177 178 static const struct drm_connector_helper_funcs corebootdrm_connector_helper_funcs = { 179 DRM_SYSFB_CONNECTOR_HELPER_FUNCS, 180 }; 181 182 static const struct drm_connector_funcs corebootdrm_connector_funcs = { 183 DRM_SYSFB_CONNECTOR_FUNCS, 184 .destroy = drm_connector_cleanup, 185 }; 186 187 static const struct drm_mode_config_funcs corebootdrm_mode_config_funcs = { 188 DRM_SYSFB_MODE_CONFIG_FUNCS, 189 }; 190 191 static int corebootdrm_mode_config_init(struct corebootdrm_device *cdev, 192 enum drm_panel_orientation orientation) 193 { 194 struct drm_sysfb_device *sysfb = &cdev->sysfb; 195 struct drm_device *dev = &sysfb->dev; 196 const struct drm_format_info *format = sysfb->fb_format; 197 unsigned int width = sysfb->fb_mode.hdisplay; 198 unsigned int height = sysfb->fb_mode.vdisplay; 199 struct drm_plane *primary_plane; 200 struct drm_crtc *crtc; 201 struct drm_encoder *encoder; 202 struct drm_connector *connector; 203 size_t nformats; 204 int ret; 205 206 ret = drmm_mode_config_init(dev); 207 if (ret) 208 return ret; 209 210 dev->mode_config.min_width = width; 211 dev->mode_config.max_width = max_t(unsigned int, width, DRM_SHADOW_PLANE_MAX_WIDTH); 212 dev->mode_config.min_height = height; 213 dev->mode_config.max_height = max_t(unsigned int, height, DRM_SHADOW_PLANE_MAX_HEIGHT); 214 dev->mode_config.funcs = &corebootdrm_mode_config_funcs; 215 dev->mode_config.preferred_depth = format->depth; 216 217 /* Primary plane */ 218 219 nformats = drm_sysfb_build_fourcc_list(dev, &format->format, 1, 220 cdev->formats, ARRAY_SIZE(cdev->formats)); 221 222 primary_plane = &cdev->primary_plane; 223 ret = drm_universal_plane_init(dev, primary_plane, 0, &corebootdrm_primary_plane_funcs, 224 cdev->formats, nformats, 225 corebootdrm_primary_plane_format_modifiers, 226 DRM_PLANE_TYPE_PRIMARY, NULL); 227 if (ret) 228 return ret; 229 drm_plane_helper_add(primary_plane, &corebootdrm_primary_plane_helper_funcs); 230 drm_plane_enable_fb_damage_clips(primary_plane); 231 232 /* CRTC */ 233 234 crtc = &cdev->crtc; 235 ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 236 &corebootdrm_crtc_funcs, NULL); 237 if (ret) 238 return ret; 239 drm_crtc_helper_add(crtc, &corebootdrm_crtc_helper_funcs); 240 241 /* Encoder */ 242 243 encoder = &cdev->encoder; 244 ret = drm_encoder_init(dev, encoder, &corebootdrm_encoder_funcs, 245 DRM_MODE_ENCODER_NONE, NULL); 246 if (ret) 247 return ret; 248 encoder->possible_crtcs = drm_crtc_mask(crtc); 249 250 /* Connector */ 251 252 connector = &cdev->connector; 253 ret = drm_connector_init(dev, connector, &corebootdrm_connector_funcs, 254 DRM_MODE_CONNECTOR_Unknown); 255 if (ret) 256 return ret; 257 drm_connector_helper_add(connector, &corebootdrm_connector_helper_funcs); 258 drm_connector_set_panel_orientation_with_quirk(connector, orientation, 259 width, height); 260 261 ret = drm_connector_attach_encoder(connector, encoder); 262 if (ret) 263 return ret; 264 265 return 0; 266 } 267 268 /* 269 * DRM driver 270 */ 271 272 DEFINE_DRM_GEM_FOPS(corebootdrm_fops); 273 274 static struct drm_driver corebootdrm_drm_driver = { 275 DRM_GEM_SHMEM_DRIVER_OPS, 276 DRM_FBDEV_SHMEM_DRIVER_OPS, 277 .name = DRIVER_NAME, 278 .desc = DRIVER_DESC, 279 .major = DRIVER_MAJOR, 280 .minor = DRIVER_MINOR, 281 .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 282 .fops = &corebootdrm_fops, 283 }; 284 285 /* 286 * Coreboot driver 287 */ 288 289 static int corebootdrm_probe(struct platform_device *pdev) 290 { 291 const struct lb_framebuffer *fb = dev_get_platdata(&pdev->dev); 292 struct corebootdrm_device *cdev; 293 struct drm_sysfb_device *sysfb; 294 struct drm_device *dev; 295 const struct drm_format_info *format; 296 int width, height, pitch; 297 resource_size_t size; 298 phys_addr_t address; 299 enum drm_panel_orientation orientation; 300 struct resource *res, *mem = NULL; 301 struct resource aperture; 302 void __iomem *screen_base; 303 int ret; 304 305 cdev = devm_drm_dev_alloc(&pdev->dev, &corebootdrm_drm_driver, 306 struct corebootdrm_device, sysfb.dev); 307 if (IS_ERR(cdev)) 308 return PTR_ERR(cdev); 309 platform_set_drvdata(pdev, cdev); 310 311 sysfb = &cdev->sysfb; 312 dev = &sysfb->dev; 313 314 if (!fb) { 315 drm_err(dev, "coreboot framebuffer not found\n"); 316 return -EINVAL; 317 } else if (!LB_FRAMEBUFFER_HAS_LFB(fb)) { 318 drm_err(dev, "coreboot framebuffer entry too small\n"); 319 return -EINVAL; 320 } 321 322 /* 323 * Hardware settings 324 */ 325 326 format = corebootdrm_get_format_fb(dev, fb); 327 if (!format) 328 return -EINVAL; 329 width = corebootdrm_get_width_fb(dev, fb); 330 if (width < 0) 331 return width; 332 height = corebootdrm_get_height_fb(dev, fb); 333 if (height < 0) 334 return height; 335 pitch = corebootdrm_get_pitch_fb(dev, format, width, fb); 336 if (pitch < 0) 337 return pitch; 338 size = corebootdrm_get_size_fb(dev, height, pitch, fb); 339 if (!size) 340 return -EINVAL; 341 address = corebootdrm_get_address_fb(dev, size, fb); 342 if (!address) 343 return -EINVAL; 344 orientation = corebootdrm_get_orientation_fb(dev, fb); 345 346 sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0); 347 sysfb->fb_format = format; 348 sysfb->fb_pitch = pitch; 349 350 drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode)); 351 drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, pitch=%d byte\n", 352 &format->format, width, height, pitch); 353 354 /* 355 * Memory management 356 */ 357 358 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 359 if (!res) { 360 drm_err(dev, "memory resource not found\n"); 361 return -EINVAL; 362 } 363 364 mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), 365 dev->driver->name); 366 if (!mem) { 367 drm_warn(dev, "could not acquire memory resource at %pr\n", res); 368 /* 369 * We cannot make this fatal. Sometimes this comes from magic 370 * spaces our resource handlers simply don't know about. Use 371 * the memory resource as-is and try to map that instead. 372 */ 373 mem = res; 374 } 375 376 drm_dbg(dev, "using memory resource at %pr\n", mem); 377 378 aperture = DEFINE_RES_MEM(address, size); 379 if (!resource_contains(mem, &aperture)) { 380 drm_err(dev, "framebuffer aperture at invalid memory range %pr\n", &aperture); 381 return -EINVAL; 382 } 383 384 ret = devm_aperture_acquire_for_platform_device(pdev, address, size); 385 if (ret) { 386 drm_err(dev, "could not acquire framebuffer aperture: %d\n", ret); 387 return ret; 388 } 389 390 screen_base = devm_ioremap_wc(&pdev->dev, address, size); 391 if (!screen_base) 392 return -ENOMEM; 393 394 iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base); 395 396 /* 397 * DRM mode setting and registration 398 */ 399 400 ret = corebootdrm_mode_config_init(cdev, orientation); 401 if (ret) 402 return ret; 403 404 drm_mode_config_reset(dev); 405 406 ret = drm_dev_register(dev, 0); 407 if (ret) 408 return ret; 409 410 drm_client_setup(dev, sysfb->fb_format); 411 412 return 0; 413 } 414 415 static void corebootdrm_remove(struct platform_device *pdev) 416 { 417 struct corebootdrm_device *cdev = platform_get_drvdata(pdev); 418 struct drm_device *dev = &cdev->sysfb.dev; 419 420 drm_dev_unplug(dev); 421 } 422 423 static struct platform_driver corebootdrm_platform_driver = { 424 .driver = { 425 .name = "coreboot-framebuffer", 426 }, 427 .probe = corebootdrm_probe, 428 .remove = corebootdrm_remove, 429 }; 430 431 module_platform_driver(corebootdrm_platform_driver); 432 433 MODULE_DESCRIPTION(DRIVER_DESC); 434 MODULE_LICENSE("GPL"); 435