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