1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * framebuffer-coreboot.c 4 * 5 * Memory based framebuffer accessed through coreboot table. 6 * 7 * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com> 8 * Copyright 2017 Google Inc. 9 * Copyright 2017 Samuel Holland <samuel@sholland.org> 10 */ 11 12 #include <linux/device.h> 13 #include <linux/kernel.h> 14 #include <linux/mm.h> 15 #include <linux/module.h> 16 #include <linux/platform_data/simplefb.h> 17 #include <linux/platform_device.h> 18 #include <linux/sysfb.h> 19 20 #include "coreboot_table.h" 21 22 #define CB_TAG_FRAMEBUFFER 0x12 23 24 static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; 25 26 static int framebuffer_probe(struct coreboot_device *dev) 27 { 28 int i; 29 u32 length; 30 struct lb_framebuffer *fb = &dev->framebuffer; 31 struct platform_device *pdev; 32 struct resource res; 33 struct simplefb_platform_data pdata = { 34 .width = fb->x_resolution, 35 .height = fb->y_resolution, 36 .stride = fb->bytes_per_line, 37 .format = NULL, 38 }; 39 40 /* 41 * On coreboot systems, the advertised LB_TAG_FRAMEBUFFER entry 42 * in the coreboot table should only be used if the payload did 43 * not pass a framebuffer information to the Linux kernel. 44 * 45 * If the global screen_info data has been filled, the Generic 46 * System Framebuffers (sysfb) will already register a platform 47 * device and pass that screen_info as platform_data to a driver 48 * that can scan-out using the system provided framebuffer. 49 */ 50 if (sysfb_handles_screen_info()) 51 return -ENODEV; 52 53 if (!fb->physical_address) 54 return -ENODEV; 55 56 for (i = 0; i < ARRAY_SIZE(formats); ++i) { 57 if (fb->bits_per_pixel == formats[i].bits_per_pixel && 58 fb->red_mask_pos == formats[i].red.offset && 59 fb->red_mask_size == formats[i].red.length && 60 fb->green_mask_pos == formats[i].green.offset && 61 fb->green_mask_size == formats[i].green.length && 62 fb->blue_mask_pos == formats[i].blue.offset && 63 fb->blue_mask_size == formats[i].blue.length) 64 pdata.format = formats[i].name; 65 } 66 if (!pdata.format) 67 return -ENODEV; 68 69 memset(&res, 0, sizeof(res)); 70 res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 71 res.name = "Coreboot Framebuffer"; 72 res.start = fb->physical_address; 73 length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); 74 res.end = res.start + length - 1; 75 if (res.end <= res.start) 76 return -EINVAL; 77 78 pdev = platform_device_register_resndata(&dev->dev, 79 "simple-framebuffer", 0, 80 &res, 1, &pdata, 81 sizeof(pdata)); 82 if (IS_ERR(pdev)) 83 pr_warn("coreboot: could not register framebuffer\n"); 84 else 85 dev_set_drvdata(&dev->dev, pdev); 86 87 return PTR_ERR_OR_ZERO(pdev); 88 } 89 90 static void framebuffer_remove(struct coreboot_device *dev) 91 { 92 struct platform_device *pdev = dev_get_drvdata(&dev->dev); 93 94 platform_device_unregister(pdev); 95 } 96 97 static const struct coreboot_device_id framebuffer_ids[] = { 98 { .tag = CB_TAG_FRAMEBUFFER }, 99 { /* sentinel */ } 100 }; 101 MODULE_DEVICE_TABLE(coreboot, framebuffer_ids); 102 103 static struct coreboot_driver framebuffer_driver = { 104 .probe = framebuffer_probe, 105 .remove = framebuffer_remove, 106 .drv = { 107 .name = "framebuffer", 108 }, 109 .id_table = framebuffer_ids, 110 }; 111 module_coreboot_driver(framebuffer_driver); 112 113 MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); 114 MODULE_DESCRIPTION("Memory based framebuffer accessed through coreboot table"); 115 MODULE_LICENSE("GPL"); 116