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 | |
19 | #include "coreboot_table.h" |
20 | |
21 | #define CB_TAG_FRAMEBUFFER 0x12 |
22 | |
23 | static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; |
24 | |
25 | static int framebuffer_probe(struct coreboot_device *dev) |
26 | { |
27 | int i; |
28 | u32 length; |
29 | struct lb_framebuffer *fb = &dev->framebuffer; |
30 | struct platform_device *pdev; |
31 | struct resource res; |
32 | struct simplefb_platform_data pdata = { |
33 | .width = fb->x_resolution, |
34 | .height = fb->y_resolution, |
35 | .stride = fb->bytes_per_line, |
36 | .format = NULL, |
37 | }; |
38 | |
39 | if (!fb->physical_address) |
40 | return -ENODEV; |
41 | |
42 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { |
43 | if (fb->bits_per_pixel == formats[i].bits_per_pixel && |
44 | fb->red_mask_pos == formats[i].red.offset && |
45 | fb->red_mask_size == formats[i].red.length && |
46 | fb->green_mask_pos == formats[i].green.offset && |
47 | fb->green_mask_size == formats[i].green.length && |
48 | fb->blue_mask_pos == formats[i].blue.offset && |
49 | fb->blue_mask_size == formats[i].blue.length) |
50 | pdata.format = formats[i].name; |
51 | } |
52 | if (!pdata.format) |
53 | return -ENODEV; |
54 | |
55 | memset(&res, 0, sizeof(res)); |
56 | res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; |
57 | res.name = "Coreboot Framebuffer" ; |
58 | res.start = fb->physical_address; |
59 | length = PAGE_ALIGN(fb->y_resolution * fb->bytes_per_line); |
60 | res.end = res.start + length - 1; |
61 | if (res.end <= res.start) |
62 | return -EINVAL; |
63 | |
64 | pdev = platform_device_register_resndata(parent: &dev->dev, |
65 | name: "simple-framebuffer" , id: 0, |
66 | res: &res, num: 1, data: &pdata, |
67 | size: sizeof(pdata)); |
68 | if (IS_ERR(ptr: pdev)) |
69 | pr_warn("coreboot: could not register framebuffer\n" ); |
70 | else |
71 | dev_set_drvdata(dev: &dev->dev, data: pdev); |
72 | |
73 | return PTR_ERR_OR_ZERO(ptr: pdev); |
74 | } |
75 | |
76 | static void framebuffer_remove(struct coreboot_device *dev) |
77 | { |
78 | struct platform_device *pdev = dev_get_drvdata(dev: &dev->dev); |
79 | |
80 | platform_device_unregister(pdev); |
81 | } |
82 | |
83 | static const struct coreboot_device_id framebuffer_ids[] = { |
84 | { .tag = CB_TAG_FRAMEBUFFER }, |
85 | { /* sentinel */ } |
86 | }; |
87 | MODULE_DEVICE_TABLE(coreboot, framebuffer_ids); |
88 | |
89 | static struct coreboot_driver framebuffer_driver = { |
90 | .probe = framebuffer_probe, |
91 | .remove = framebuffer_remove, |
92 | .drv = { |
93 | .name = "framebuffer" , |
94 | }, |
95 | .id_table = framebuffer_ids, |
96 | }; |
97 | module_coreboot_driver(framebuffer_driver); |
98 | |
99 | MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>" ); |
100 | MODULE_LICENSE("GPL" ); |
101 | |