| /* | |
| * framebuffer-coreboot.c | |
| * | |
| * Memory based framebuffer accessed through coreboot table. | |
| * | |
| * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com> | |
| * Copyright 2017 Google Inc. | |
| * Copyright 2017 Samuel Holland <samuel@sholland.org> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License v2.0 as published by | |
| * the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| #include <linux/device.h> | |
| #include <linux/kernel.h> | |
| #include <linux/mm.h> | |
| #include <linux/module.h> | |
| #include <linux/platform_data/simplefb.h> | |
| #include <linux/platform_device.h> | |
| #include "coreboot_table.h" | |
| #define CB_TAG_FRAMEBUFFER 0x12 | |
| static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; | |
| static int framebuffer_probe(struct coreboot_device *dev) | |
| { | |
| int i; | |
| u32 length; | |
| struct lb_framebuffer *fb = &dev->framebuffer; | |
| struct platform_device *pdev; | |
| struct resource res; | |
| struct simplefb_platform_data pdata = { | |
| .width = fb->x_resolution, | |
| .height = fb->y_resolution, | |
| .stride = fb->bytes_per_line, | |
| .format = NULL, | |
| }; | |
| for (i = 0; i < ARRAY_SIZE(formats); ++i) { | |
| if (fb->bits_per_pixel == formats[i].bits_per_pixel && | |
| fb->red_mask_pos == formats[i].red.offset && | |
| fb->red_mask_size == formats[i].red.length && | |
| fb->green_mask_pos == formats[i].green.offset && | |
| fb->green_mask_size == formats[i].green.length && | |
| fb->blue_mask_pos == formats[i].blue.offset && | |
| fb->blue_mask_size == formats[i].blue.length && | |
| fb->reserved_mask_pos == formats[i].transp.offset && | |
| fb->reserved_mask_size == formats[i].transp.length) | |
| pdata.format = formats[i].name; | |
| } | |
| if (!pdata.format) | |
| return -ENODEV; | |
| memset(&res, 0, sizeof(res)); | |
| res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; | |
| res.name = "Coreboot Framebuffer"; | |
| res.start = fb->physical_address; | |
| length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); | |
| res.end = res.start + length - 1; | |
| if (res.end <= res.start) | |
| return -EINVAL; | |
| pdev = platform_device_register_resndata(&dev->dev, | |
| "simple-framebuffer", 0, | |
| &res, 1, &pdata, | |
| sizeof(pdata)); | |
| if (IS_ERR(pdev)) | |
| pr_warn("coreboot: could not register framebuffer\n"); | |
| else | |
| dev_set_drvdata(&dev->dev, pdev); | |
| return PTR_ERR_OR_ZERO(pdev); | |
| } | |
| static int framebuffer_remove(struct coreboot_device *dev) | |
| { | |
| struct platform_device *pdev = dev_get_drvdata(&dev->dev); | |
| platform_device_unregister(pdev); | |
| return 0; | |
| } | |
| static struct coreboot_driver framebuffer_driver = { | |
| .probe = framebuffer_probe, | |
| .remove = framebuffer_remove, | |
| .drv = { | |
| .name = "framebuffer", | |
| }, | |
| .tag = CB_TAG_FRAMEBUFFER, | |
| }; | |
| static int __init coreboot_framebuffer_init(void) | |
| { | |
| return coreboot_driver_register(&framebuffer_driver); | |
| } | |
| static void coreboot_framebuffer_exit(void) | |
| { | |
| coreboot_driver_unregister(&framebuffer_driver); | |
| } | |
| module_init(coreboot_framebuffer_init); | |
| module_exit(coreboot_framebuffer_exit); | |
| MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); | |
| MODULE_LICENSE("GPL"); |