xref: /linux/drivers/video/screen_info_generic.c (revision 44343e8b250abb2f6bfd615493ca07a7f11f3cc2)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/export.h>
4 #include <linux/ioport.h>
5 #include <linux/screen_info.h>
6 #include <linux/string.h>
7 
8 #include <video/pixel_format.h>
9 
10 static void resource_init_named(struct resource *r,
11 				resource_size_t start, resource_size_t size,
12 				const char *name, unsigned int flags)
13 {
14 	memset(r, 0, sizeof(*r));
15 
16 	r->start = start;
17 	r->end = start + size - 1;
18 	r->name = name;
19 	r->flags = flags;
20 }
21 
22 static void resource_init_io_named(struct resource *r,
23 				   resource_size_t start, resource_size_t size,
24 				   const char *name)
25 {
26 	resource_init_named(r, start, size, name, IORESOURCE_IO);
27 }
28 
29 static void resource_init_mem_named(struct resource *r,
30 				   resource_size_t start, resource_size_t size,
31 				   const char *name)
32 {
33 	resource_init_named(r, start, size, name, IORESOURCE_MEM);
34 }
35 
36 static inline bool __screen_info_has_ega_gfx(unsigned int mode)
37 {
38 	switch (mode) {
39 	case 0x0d:	/* 320x200-4 */
40 	case 0x0e:	/* 640x200-4 */
41 	case 0x0f:	/* 640x350-1 */
42 	case 0x10:	/* 640x350-4 */
43 		return true;
44 	default:
45 		return false;
46 	}
47 }
48 
49 static inline bool __screen_info_has_vga_gfx(unsigned int mode)
50 {
51 	switch (mode) {
52 	case 0x10:	/* 640x480-1 */
53 	case 0x12:	/* 640x480-4 */
54 	case 0x13:	/* 320-200-8 */
55 	case 0x6a:	/* 800x600-4 (VESA) */
56 		return true;
57 	default:
58 		return __screen_info_has_ega_gfx(mode);
59 	}
60 }
61 
62 /**
63  * screen_info_resources() - Get resources from screen_info structure
64  * @si: the screen_info
65  * @r: pointer to an array of resource structures
66  * @num: number of elements in @r:
67  *
68  * Returns:
69  * The number of resources stored in @r on success, or a negative errno code otherwise.
70  *
71  * A call to screen_info_resources() returns the resources consumed by the
72  * screen_info's device or framebuffer. The result is stored in the caller-supplied
73  * array @r with up to @num elements. The function returns the number of
74  * initialized elements.
75  */
76 ssize_t screen_info_resources(const struct screen_info *si, struct resource *r, size_t num)
77 {
78 	struct resource *pos = r;
79 	unsigned int type = screen_info_video_type(si);
80 	u64 base, size;
81 
82 	switch (type) {
83 	case VIDEO_TYPE_MDA:
84 		if (num > 0)
85 			resource_init_io_named(pos++, 0x3b0, 12, "mda");
86 		if (num > 1)
87 			resource_init_io_named(pos++, 0x3bf, 0x01, "mda");
88 		if (num > 2)
89 			resource_init_mem_named(pos++, 0xb0000, 0x2000, "mda");
90 		break;
91 	case VIDEO_TYPE_CGA:
92 		if (num > 0)
93 			resource_init_io_named(pos++, 0x3d4, 0x02, "cga");
94 		if (num > 1)
95 			resource_init_mem_named(pos++, 0xb8000, 0x2000, "cga");
96 		break;
97 	case VIDEO_TYPE_EGAM:
98 		if (num > 0)
99 			resource_init_io_named(pos++, 0x3bf, 0x10, "ega");
100 		if (num > 1)
101 			resource_init_mem_named(pos++, 0xb0000, 0x8000, "ega");
102 		break;
103 	case VIDEO_TYPE_EGAC:
104 		if (num > 0)
105 			resource_init_io_named(pos++, 0x3c0, 0x20, "ega");
106 		if (num > 1) {
107 			if (__screen_info_has_ega_gfx(si->orig_video_mode))
108 				resource_init_mem_named(pos++, 0xa0000, 0x10000, "ega");
109 			else
110 				resource_init_mem_named(pos++, 0xb8000, 0x8000, "ega");
111 		}
112 		break;
113 	case VIDEO_TYPE_VGAC:
114 		if (num > 0)
115 			resource_init_io_named(pos++, 0x3c0, 0x20, "vga+");
116 		if (num > 1) {
117 			if (__screen_info_has_vga_gfx(si->orig_video_mode))
118 				resource_init_mem_named(pos++, 0xa0000, 0x10000, "vga+");
119 			else
120 				resource_init_mem_named(pos++, 0xb8000, 0x8000, "vga+");
121 		}
122 		break;
123 	case VIDEO_TYPE_VLFB:
124 	case VIDEO_TYPE_EFI:
125 		base = __screen_info_lfb_base(si);
126 		if (!base)
127 			break;
128 		size = __screen_info_lfb_size(si, type);
129 		if (!size)
130 			break;
131 		if (num > 0)
132 			resource_init_mem_named(pos++, base, size, "lfb");
133 		break;
134 	case VIDEO_TYPE_PICA_S3:
135 	case VIDEO_TYPE_MIPS_G364:
136 	case VIDEO_TYPE_SGI:
137 	case VIDEO_TYPE_TGAC:
138 	case VIDEO_TYPE_SUN:
139 	case VIDEO_TYPE_SUNPCI:
140 	case VIDEO_TYPE_PMAC:
141 	default:
142 		/* not supported */
143 		return -EINVAL;
144 	}
145 
146 	return pos - r;
147 }
148 EXPORT_SYMBOL(screen_info_resources);
149 
150 /*
151  * The meaning of depth and bpp for direct-color formats is
152  * inconsistent:
153  *
154  *  - DRM format info specifies depth as the number of color
155  *    bits; including alpha, but not including filler bits.
156  *  - Linux' EFI platform code computes lfb_depth from the
157  *    individual color channels, including the reserved bits.
158  *  - VBE 1.1 defines lfb_depth for XRGB1555 as 16, but later
159  *    versions use 15.
160  *  - On the kernel command line, 'bpp' of 32 is usually
161  *    XRGB8888 including the filler bits, but 15 is XRGB1555
162  *    not including the filler bit.
163  *
164  * It is not easily possible to fix this in struct screen_info,
165  * as this could break UAPI. The best solution is to compute
166  * bits_per_pixel from the color bits, reserved bits and
167  * reported lfb_depth, whichever is highest.
168  */
169 
170 u32 __screen_info_lfb_bits_per_pixel(const struct screen_info *si)
171 {
172 	u32 bits_per_pixel = si->lfb_depth;
173 
174 	if (bits_per_pixel > 8) {
175 		bits_per_pixel = max(max3(si->red_size + si->red_pos,
176 					  si->green_size + si->green_pos,
177 					  si->blue_size + si->blue_pos),
178 				     si->rsvd_size + si->rsvd_pos);
179 		bits_per_pixel = max_t(u32, bits_per_pixel, si->lfb_depth);
180 	}
181 
182 	return bits_per_pixel;
183 }
184 EXPORT_SYMBOL(__screen_info_lfb_bits_per_pixel);
185 
186 static int __screen_info_lfb_pixel_format(const struct screen_info *si, struct pixel_format *f)
187 {
188 	u32 bits_per_pixel = __screen_info_lfb_bits_per_pixel(si);
189 
190 	if (bits_per_pixel > U8_MAX)
191 		return -EINVAL;
192 
193 	f->bits_per_pixel = bits_per_pixel;
194 
195 	if (si->lfb_depth > 8) {
196 		f->indexed = false;
197 		f->alpha.offset = 0;
198 		f->alpha.length = 0;
199 		f->red.offset = si->red_pos;
200 		f->red.length = si->red_size;
201 		f->green.offset = si->green_pos;
202 		f->green.length = si->green_size;
203 		f->blue.offset = si->blue_pos;
204 		f->blue.length = si->blue_size;
205 	} else {
206 		f->indexed = true;
207 		f->index.offset = 0;
208 		f->index.length = si->lfb_depth;
209 	}
210 
211 	return 0;
212 }
213 
214 /**
215  * screen_info_pixel_format - Returns the screen-info format as pixel-format description
216  *
217  * @si: the screen_info
218  * @f: pointer to return pixel-format description
219  *
220  * Returns:
221  * 0 on success, or a negative errno code otherwise.
222  */
223 int screen_info_pixel_format(const struct screen_info *si, struct pixel_format *f)
224 {
225 	unsigned int type = screen_info_video_type(si);
226 
227 	/* TODO: Add support for additional types as needed. */
228 	switch (type) {
229 	case VIDEO_TYPE_VLFB:
230 	case VIDEO_TYPE_EFI:
231 		return __screen_info_lfb_pixel_format(si, f);
232 	}
233 
234 	/* not supported */
235 	return -EINVAL;
236 }
237 EXPORT_SYMBOL(screen_info_pixel_format);
238