brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 0bab835 Raw
509 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/fb.h>4#include <linux/linux_logo.h>5 6#include "fb_internal.h"7 8bool fb_center_logo __read_mostly;9int fb_logo_count __read_mostly = -1;10 11static inline unsigned int safe_shift(unsigned int d, int n)12{13	return n < 0 ? d >> -n : d << n;14}15 16static void fb_set_logocmap(struct fb_info *info,17				   const struct linux_logo *logo)18{19	struct fb_cmap palette_cmap;20	u16 palette_green[16];21	u16 palette_blue[16];22	u16 palette_red[16];23	int i, j, n;24	const unsigned char *clut = logo->clut;25 26	palette_cmap.start = 0;27	palette_cmap.len = 16;28	palette_cmap.red = palette_red;29	palette_cmap.green = palette_green;30	palette_cmap.blue = palette_blue;31	palette_cmap.transp = NULL;32 33	for (i = 0; i < logo->clutsize; i += n) {34		n = logo->clutsize - i;35		/* palette_cmap provides space for only 16 colors at once */36		if (n > 16)37			n = 16;38		palette_cmap.start = 32 + i;39		palette_cmap.len = n;40		for (j = 0; j < n; ++j) {41			palette_cmap.red[j] = clut[0] << 8 | clut[0];42			palette_cmap.green[j] = clut[1] << 8 | clut[1];43			palette_cmap.blue[j] = clut[2] << 8 | clut[2];44			clut += 3;45		}46		fb_set_cmap(&palette_cmap, info);47	}48}49 50static void  fb_set_logo_truepalette(struct fb_info *info,51					    const struct linux_logo *logo,52					    u32 *palette)53{54	static const unsigned char mask[] = {55		0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff56	};57	unsigned char redmask, greenmask, bluemask;58	int redshift, greenshift, blueshift;59	int i;60	const unsigned char *clut = logo->clut;61 62	/*63	 * We have to create a temporary palette since console palette is only64	 * 16 colors long.65	 */66	/* Bug: Doesn't obey msb_right ... (who needs that?) */67	redmask   = mask[info->var.red.length   < 8 ? info->var.red.length   : 8];68	greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];69	bluemask  = mask[info->var.blue.length  < 8 ? info->var.blue.length  : 8];70	redshift   = info->var.red.offset   - (8 - info->var.red.length);71	greenshift = info->var.green.offset - (8 - info->var.green.length);72	blueshift  = info->var.blue.offset  - (8 - info->var.blue.length);73 74	for (i = 0; i < logo->clutsize; i++) {75		palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |76				 safe_shift((clut[1] & greenmask), greenshift) |77				 safe_shift((clut[2] & bluemask), blueshift));78		clut += 3;79	}80}81 82static void fb_set_logo_directpalette(struct fb_info *info,83					     const struct linux_logo *logo,84					     u32 *palette)85{86	int redshift, greenshift, blueshift;87	int i;88 89	redshift = info->var.red.offset;90	greenshift = info->var.green.offset;91	blueshift = info->var.blue.offset;92 93	for (i = 32; i < 32 + logo->clutsize; i++)94		palette[i] = i << redshift | i << greenshift | i << blueshift;95}96 97static void fb_set_logo(struct fb_info *info,98			       const struct linux_logo *logo, u8 *dst,99			       int depth)100{101	int i, j, k;102	const u8 *src = logo->data;103	u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;104	u8 fg = 1, d;105 106	switch (fb_get_color_depth(&info->var, &info->fix)) {107	case 1:108		fg = 1;109		break;110	case 2:111		fg = 3;112		break;113	default:114		fg = 7;115		break;116	}117 118	if (info->fix.visual == FB_VISUAL_MONO01 ||119	    info->fix.visual == FB_VISUAL_MONO10)120		fg = ~((u8) (0xfff << info->var.green.length));121 122	switch (depth) {123	case 4:124		for (i = 0; i < logo->height; i++)125			for (j = 0; j < logo->width; src++) {126				*dst++ = *src >> 4;127				j++;128				if (j < logo->width) {129					*dst++ = *src & 0x0f;130					j++;131				}132			}133		break;134	case 1:135		for (i = 0; i < logo->height; i++) {136			for (j = 0; j < logo->width; src++) {137				d = *src ^ xor;138				for (k = 7; k >= 0 && j < logo->width; k--) {139					*dst++ = ((d >> k) & 1) ? fg : 0;140					j++;141				}142			}143		}144		break;145	}146}147 148/*149 * Three (3) kinds of logo maps exist.  linux_logo_clut224 (>16 colors),150 * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors).  Depending on151 * the visual format and color depth of the framebuffer, the DAC, the152 * pseudo_palette, and the logo data will be adjusted accordingly.153 *154 * Case 1 - linux_logo_clut224:155 * Color exceeds the number of console colors (16), thus we set the hardware DAC156 * using fb_set_cmap() appropriately.  The "needs_cmapreset"  flag will be set.157 *158 * For visuals that require color info from the pseudo_palette, we also construct159 * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags160 * will be set.161 *162 * Case 2 - linux_logo_vga16:163 * The number of colors just matches the console colors, thus there is no need164 * to set the DAC or the pseudo_palette.  However, the bitmap is packed, ie,165 * each byte contains color information for two pixels (upper and lower nibble).166 * To be consistent with fb_imageblit() usage, we therefore separate the two167 * nibbles into separate bytes. The "depth" flag will be set to 4.168 *169 * Case 3 - linux_logo_mono:170 * This is similar with Case 2.  Each byte contains information for 8 pixels.171 * We isolate each bit and expand each into a byte. The "depth" flag will172 * be set to 1.173 */174static struct logo_data {175	int depth;176	int needs_directpalette;177	int needs_truepalette;178	int needs_cmapreset;179	const struct linux_logo *logo;180} fb_logo __read_mostly;181 182static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)183{184	u32 size = width * height, i;185 186	out += size - 1;187 188	for (i = size; i--; )189		*out-- = *in++;190}191 192static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)193{194	int i, j, h = height - 1;195 196	for (i = 0; i < height; i++)197		for (j = 0; j < width; j++)198			out[height * j + h - i] = *in++;199}200 201static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)202{203	int i, j, w = width - 1;204 205	for (i = 0; i < height; i++)206		for (j = 0; j < width; j++)207			out[height * (w - j) + i] = *in++;208}209 210static void fb_rotate_logo(struct fb_info *info, u8 *dst,211			   struct fb_image *image, int rotate)212{213	u32 tmp;214 215	if (rotate == FB_ROTATE_UD) {216		fb_rotate_logo_ud(image->data, dst, image->width,217				  image->height);218		image->dx = info->var.xres - image->width - image->dx;219		image->dy = info->var.yres - image->height - image->dy;220	} else if (rotate == FB_ROTATE_CW) {221		fb_rotate_logo_cw(image->data, dst, image->width,222				  image->height);223		swap(image->width, image->height);224		tmp = image->dy;225		image->dy = image->dx;226		image->dx = info->var.xres - image->width - tmp;227	} else if (rotate == FB_ROTATE_CCW) {228		fb_rotate_logo_ccw(image->data, dst, image->width,229				   image->height);230		swap(image->width, image->height);231		tmp = image->dx;232		image->dx = image->dy;233		image->dy = info->var.yres - image->height - tmp;234	}235 236	image->data = dst;237}238 239static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,240			    int rotate, unsigned int num)241{242	unsigned int x;243 244	if (image->width > info->var.xres || image->height > info->var.yres)245		return;246 247	if (rotate == FB_ROTATE_UR) {248		for (x = 0;249		     x < num && image->dx + image->width <= info->var.xres;250		     x++) {251			info->fbops->fb_imageblit(info, image);252			image->dx += image->width + 8;253		}254	} else if (rotate == FB_ROTATE_UD) {255		u32 dx = image->dx;256 257		for (x = 0; x < num && image->dx <= dx; x++) {258			info->fbops->fb_imageblit(info, image);259			image->dx -= image->width + 8;260		}261	} else if (rotate == FB_ROTATE_CW) {262		for (x = 0;263		     x < num && image->dy + image->height <= info->var.yres;264		     x++) {265			info->fbops->fb_imageblit(info, image);266			image->dy += image->height + 8;267		}268	} else if (rotate == FB_ROTATE_CCW) {269		u32 dy = image->dy;270 271		for (x = 0; x < num && image->dy <= dy; x++) {272			info->fbops->fb_imageblit(info, image);273			image->dy -= image->height + 8;274		}275	}276}277 278static int fb_show_logo_line(struct fb_info *info, int rotate,279			     const struct linux_logo *logo, int y,280			     unsigned int n)281{282	u32 *palette = NULL, *saved_pseudo_palette = NULL;283	unsigned char *logo_new = NULL, *logo_rotate = NULL;284	struct fb_image image;285 286	/* Return if the frame buffer is not mapped or suspended */287	if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||288	    info->fbops->owner)289		return 0;290 291	image.depth = 8;292	image.data = logo->data;293 294	if (fb_logo.needs_cmapreset)295		fb_set_logocmap(info, logo);296 297	if (fb_logo.needs_truepalette ||298	    fb_logo.needs_directpalette) {299		palette = kmalloc(256 * 4, GFP_KERNEL);300		if (palette == NULL)301			return 0;302 303		if (fb_logo.needs_truepalette)304			fb_set_logo_truepalette(info, logo, palette);305		else306			fb_set_logo_directpalette(info, logo, palette);307 308		saved_pseudo_palette = info->pseudo_palette;309		info->pseudo_palette = palette;310	}311 312	if (fb_logo.depth <= 4) {313		logo_new = kmalloc_array(logo->width, logo->height,314					 GFP_KERNEL);315		if (logo_new == NULL) {316			kfree(palette);317			if (saved_pseudo_palette)318				info->pseudo_palette = saved_pseudo_palette;319			return 0;320		}321		image.data = logo_new;322		fb_set_logo(info, logo, logo_new, fb_logo.depth);323	}324 325	if (fb_center_logo) {326		int xres = info->var.xres;327		int yres = info->var.yres;328 329		if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) {330			xres = info->var.yres;331			yres = info->var.xres;332		}333 334		while (n && (n * (logo->width + 8) - 8 > xres))335			--n;336		image.dx = (xres - (n * (logo->width + 8) - 8)) / 2;337		image.dy = y ?: (yres - logo->height) / 2;338	} else {339		image.dx = 0;340		image.dy = y;341	}342 343	image.width = logo->width;344	image.height = logo->height;345 346	if (rotate) {347		logo_rotate = kmalloc_array(logo->width, logo->height,348					    GFP_KERNEL);349		if (logo_rotate)350			fb_rotate_logo(info, logo_rotate, &image, rotate);351	}352 353	fb_do_show_logo(info, &image, rotate, n);354 355	kfree(palette);356	if (saved_pseudo_palette != NULL)357		info->pseudo_palette = saved_pseudo_palette;358	kfree(logo_new);359	kfree(logo_rotate);360	return image.dy + logo->height;361}362 363#ifdef CONFIG_FB_LOGO_EXTRA364 365#define FB_LOGO_EX_NUM_MAX 10366static struct logo_data_extra {367	const struct linux_logo *logo;368	unsigned int n;369} fb_logo_ex[FB_LOGO_EX_NUM_MAX];370static unsigned int fb_logo_ex_num;371 372void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)373{374	if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)375		return;376 377	fb_logo_ex[fb_logo_ex_num].logo = logo;378	fb_logo_ex[fb_logo_ex_num].n = n;379	fb_logo_ex_num++;380}381 382static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,383				  unsigned int yres)384{385	unsigned int i;386 387	/* FIXME: logo_ex supports only truecolor fb. */388	if (info->fix.visual != FB_VISUAL_TRUECOLOR)389		fb_logo_ex_num = 0;390 391	for (i = 0; i < fb_logo_ex_num; i++) {392		if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {393			fb_logo_ex[i].logo = NULL;394			continue;395		}396		height += fb_logo_ex[i].logo->height;397		if (height > yres) {398			height -= fb_logo_ex[i].logo->height;399			fb_logo_ex_num = i;400			break;401		}402	}403	return height;404}405 406static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)407{408	unsigned int i;409 410	for (i = 0; i < fb_logo_ex_num; i++)411		y = fb_show_logo_line(info, rotate,412				      fb_logo_ex[i].logo, y, fb_logo_ex[i].n);413 414	return y;415}416#endif /* CONFIG_FB_LOGO_EXTRA */417 418int fb_prepare_logo(struct fb_info *info, int rotate)419{420	int depth = fb_get_color_depth(&info->var, &info->fix);421	unsigned int yres;422	int height;423 424	memset(&fb_logo, 0, sizeof(struct logo_data));425 426	if (info->flags & FBINFO_MISC_TILEBLITTING ||427	    info->fbops->owner || !fb_logo_count)428		return 0;429 430	if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {431		depth = info->var.blue.length;432		if (info->var.red.length < depth)433			depth = info->var.red.length;434		if (info->var.green.length < depth)435			depth = info->var.green.length;436	}437 438	if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {439		/* assume console colormap */440		depth = 4;441	}442 443	/* Return if no suitable logo was found */444	fb_logo.logo = fb_find_logo(depth);445 446	if (!fb_logo.logo)447		return 0;448 449	if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)450		yres = info->var.yres;451	else452		yres = info->var.xres;453 454	if (fb_logo.logo->height > yres) {455		fb_logo.logo = NULL;456		return 0;457	}458 459	/* What depth we asked for might be different from what we get */460	if (fb_logo.logo->type == LINUX_LOGO_CLUT224)461		fb_logo.depth = 8;462	else if (fb_logo.logo->type == LINUX_LOGO_VGA16)463		fb_logo.depth = 4;464	else465		fb_logo.depth = 1;466 467 468	if (fb_logo.depth > 4 && depth > 4) {469		switch (info->fix.visual) {470		case FB_VISUAL_TRUECOLOR:471			fb_logo.needs_truepalette = 1;472			break;473		case FB_VISUAL_DIRECTCOLOR:474			fb_logo.needs_directpalette = 1;475			fb_logo.needs_cmapreset = 1;476			break;477		case FB_VISUAL_PSEUDOCOLOR:478			fb_logo.needs_cmapreset = 1;479			break;480		}481	}482 483	height = fb_logo.logo->height;484	if (fb_center_logo)485		height += (yres - fb_logo.logo->height) / 2;486#ifdef CONFIG_FB_LOGO_EXTRA487	height = fb_prepare_extra_logos(info, height, yres);488#endif489 490	return height;491}492 493int fb_show_logo(struct fb_info *info, int rotate)494{495	unsigned int count;496	int y;497 498	if (!fb_logo_count)499		return 0;500 501	count = fb_logo_count < 0 ? num_online_cpus() : fb_logo_count;502	y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, count);503#ifdef CONFIG_FB_LOGO_EXTRA504	y = fb_show_extra_logos(info, y, rotate);505#endif506 507	return y;508}509