0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/module.h>
0015
0016 #include <linux/compat.h>
0017 #include <linux/types.h>
0018 #include <linux/errno.h>
0019 #include <linux/kernel.h>
0020 #include <linux/major.h>
0021 #include <linux/slab.h>
0022 #include <linux/sysfb.h>
0023 #include <linux/mm.h>
0024 #include <linux/mman.h>
0025 #include <linux/vt.h>
0026 #include <linux/init.h>
0027 #include <linux/linux_logo.h>
0028 #include <linux/proc_fs.h>
0029 #include <linux/platform_device.h>
0030 #include <linux/seq_file.h>
0031 #include <linux/console.h>
0032 #include <linux/kmod.h>
0033 #include <linux/err.h>
0034 #include <linux/device.h>
0035 #include <linux/efi.h>
0036 #include <linux/fb.h>
0037 #include <linux/fbcon.h>
0038 #include <linux/mem_encrypt.h>
0039 #include <linux/pci.h>
0040
0041 #include <asm/fb.h>
0042
0043
0044
0045
0046
0047
0048 #define FBPIXMAPSIZE (1024 * 8)
0049
0050 static DEFINE_MUTEX(registration_lock);
0051
0052 struct fb_info *registered_fb[FB_MAX] __read_mostly;
0053 EXPORT_SYMBOL(registered_fb);
0054
0055 int num_registered_fb __read_mostly;
0056 EXPORT_SYMBOL(num_registered_fb);
0057
0058 bool fb_center_logo __read_mostly;
0059
0060 int fb_logo_count __read_mostly = -1;
0061
0062 static struct fb_info *get_fb_info(unsigned int idx)
0063 {
0064 struct fb_info *fb_info;
0065
0066 if (idx >= FB_MAX)
0067 return ERR_PTR(-ENODEV);
0068
0069 mutex_lock(®istration_lock);
0070 fb_info = registered_fb[idx];
0071 if (fb_info)
0072 refcount_inc(&fb_info->count);
0073 mutex_unlock(®istration_lock);
0074
0075 return fb_info;
0076 }
0077
0078 static void put_fb_info(struct fb_info *fb_info)
0079 {
0080 if (!refcount_dec_and_test(&fb_info->count))
0081 return;
0082 if (fb_info->fbops->fb_destroy)
0083 fb_info->fbops->fb_destroy(fb_info);
0084 }
0085
0086
0087
0088
0089
0090 int fb_get_color_depth(struct fb_var_screeninfo *var,
0091 struct fb_fix_screeninfo *fix)
0092 {
0093 int depth = 0;
0094
0095 if (fix->visual == FB_VISUAL_MONO01 ||
0096 fix->visual == FB_VISUAL_MONO10)
0097 depth = 1;
0098 else {
0099 if (var->green.length == var->blue.length &&
0100 var->green.length == var->red.length &&
0101 var->green.offset == var->blue.offset &&
0102 var->green.offset == var->red.offset)
0103 depth = var->green.length;
0104 else
0105 depth = var->green.length + var->red.length +
0106 var->blue.length;
0107 }
0108
0109 return depth;
0110 }
0111 EXPORT_SYMBOL(fb_get_color_depth);
0112
0113
0114
0115
0116 void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
0117 {
0118 __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
0119 }
0120 EXPORT_SYMBOL(fb_pad_aligned_buffer);
0121
0122 void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
0123 u32 shift_high, u32 shift_low, u32 mod)
0124 {
0125 u8 mask = (u8) (0xfff << shift_high), tmp;
0126 int i, j;
0127
0128 for (i = height; i--; ) {
0129 for (j = 0; j < idx; j++) {
0130 tmp = dst[j];
0131 tmp &= mask;
0132 tmp |= *src >> shift_low;
0133 dst[j] = tmp;
0134 tmp = *src << shift_high;
0135 dst[j+1] = tmp;
0136 src++;
0137 }
0138 tmp = dst[idx];
0139 tmp &= mask;
0140 tmp |= *src >> shift_low;
0141 dst[idx] = tmp;
0142 if (shift_high < mod) {
0143 tmp = *src << shift_high;
0144 dst[idx+1] = tmp;
0145 }
0146 src++;
0147 dst += d_pitch;
0148 }
0149 }
0150 EXPORT_SYMBOL(fb_pad_unaligned_buffer);
0151
0152
0153
0154
0155
0156 char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
0157 {
0158 u32 align = buf->buf_align - 1, offset;
0159 char *addr = buf->addr;
0160
0161
0162
0163
0164 if (buf->flags & FB_PIXMAP_IO) {
0165 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
0166 info->fbops->fb_sync(info);
0167 return addr;
0168 }
0169
0170
0171 offset = buf->offset + align;
0172 offset &= ~align;
0173 if (offset + size > buf->size) {
0174
0175
0176
0177
0178 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
0179 info->fbops->fb_sync(info);
0180 offset = 0;
0181 }
0182 buf->offset = offset + size;
0183 addr += offset;
0184
0185 return addr;
0186 }
0187 EXPORT_SYMBOL(fb_get_buffer_offset);
0188
0189 #ifdef CONFIG_LOGO
0190
0191 static inline unsigned safe_shift(unsigned d, int n)
0192 {
0193 return n < 0 ? d >> -n : d << n;
0194 }
0195
0196 static void fb_set_logocmap(struct fb_info *info,
0197 const struct linux_logo *logo)
0198 {
0199 struct fb_cmap palette_cmap;
0200 u16 palette_green[16];
0201 u16 palette_blue[16];
0202 u16 palette_red[16];
0203 int i, j, n;
0204 const unsigned char *clut = logo->clut;
0205
0206 palette_cmap.start = 0;
0207 palette_cmap.len = 16;
0208 palette_cmap.red = palette_red;
0209 palette_cmap.green = palette_green;
0210 palette_cmap.blue = palette_blue;
0211 palette_cmap.transp = NULL;
0212
0213 for (i = 0; i < logo->clutsize; i += n) {
0214 n = logo->clutsize - i;
0215
0216 if (n > 16)
0217 n = 16;
0218 palette_cmap.start = 32 + i;
0219 palette_cmap.len = n;
0220 for (j = 0; j < n; ++j) {
0221 palette_cmap.red[j] = clut[0] << 8 | clut[0];
0222 palette_cmap.green[j] = clut[1] << 8 | clut[1];
0223 palette_cmap.blue[j] = clut[2] << 8 | clut[2];
0224 clut += 3;
0225 }
0226 fb_set_cmap(&palette_cmap, info);
0227 }
0228 }
0229
0230 static void fb_set_logo_truepalette(struct fb_info *info,
0231 const struct linux_logo *logo,
0232 u32 *palette)
0233 {
0234 static const unsigned char mask[] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
0235 unsigned char redmask, greenmask, bluemask;
0236 int redshift, greenshift, blueshift;
0237 int i;
0238 const unsigned char *clut = logo->clut;
0239
0240
0241
0242
0243
0244
0245 redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8];
0246 greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8];
0247 bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8];
0248 redshift = info->var.red.offset - (8 - info->var.red.length);
0249 greenshift = info->var.green.offset - (8 - info->var.green.length);
0250 blueshift = info->var.blue.offset - (8 - info->var.blue.length);
0251
0252 for ( i = 0; i < logo->clutsize; i++) {
0253 palette[i+32] = (safe_shift((clut[0] & redmask), redshift) |
0254 safe_shift((clut[1] & greenmask), greenshift) |
0255 safe_shift((clut[2] & bluemask), blueshift));
0256 clut += 3;
0257 }
0258 }
0259
0260 static void fb_set_logo_directpalette(struct fb_info *info,
0261 const struct linux_logo *logo,
0262 u32 *palette)
0263 {
0264 int redshift, greenshift, blueshift;
0265 int i;
0266
0267 redshift = info->var.red.offset;
0268 greenshift = info->var.green.offset;
0269 blueshift = info->var.blue.offset;
0270
0271 for (i = 32; i < 32 + logo->clutsize; i++)
0272 palette[i] = i << redshift | i << greenshift | i << blueshift;
0273 }
0274
0275 static void fb_set_logo(struct fb_info *info,
0276 const struct linux_logo *logo, u8 *dst,
0277 int depth)
0278 {
0279 int i, j, k;
0280 const u8 *src = logo->data;
0281 u8 xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
0282 u8 fg = 1, d;
0283
0284 switch (fb_get_color_depth(&info->var, &info->fix)) {
0285 case 1:
0286 fg = 1;
0287 break;
0288 case 2:
0289 fg = 3;
0290 break;
0291 default:
0292 fg = 7;
0293 break;
0294 }
0295
0296 if (info->fix.visual == FB_VISUAL_MONO01 ||
0297 info->fix.visual == FB_VISUAL_MONO10)
0298 fg = ~((u8) (0xfff << info->var.green.length));
0299
0300 switch (depth) {
0301 case 4:
0302 for (i = 0; i < logo->height; i++)
0303 for (j = 0; j < logo->width; src++) {
0304 *dst++ = *src >> 4;
0305 j++;
0306 if (j < logo->width) {
0307 *dst++ = *src & 0x0f;
0308 j++;
0309 }
0310 }
0311 break;
0312 case 1:
0313 for (i = 0; i < logo->height; i++) {
0314 for (j = 0; j < logo->width; src++) {
0315 d = *src ^ xor;
0316 for (k = 7; k >= 0 && j < logo->width; k--) {
0317 *dst++ = ((d >> k) & 1) ? fg : 0;
0318 j++;
0319 }
0320 }
0321 }
0322 break;
0323 }
0324 }
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352 static struct logo_data {
0353 int depth;
0354 int needs_directpalette;
0355 int needs_truepalette;
0356 int needs_cmapreset;
0357 const struct linux_logo *logo;
0358 } fb_logo __read_mostly;
0359
0360 static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
0361 {
0362 u32 size = width * height, i;
0363
0364 out += size - 1;
0365
0366 for (i = size; i--; )
0367 *out-- = *in++;
0368 }
0369
0370 static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
0371 {
0372 int i, j, h = height - 1;
0373
0374 for (i = 0; i < height; i++)
0375 for (j = 0; j < width; j++)
0376 out[height * j + h - i] = *in++;
0377 }
0378
0379 static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
0380 {
0381 int i, j, w = width - 1;
0382
0383 for (i = 0; i < height; i++)
0384 for (j = 0; j < width; j++)
0385 out[height * (w - j) + i] = *in++;
0386 }
0387
0388 static void fb_rotate_logo(struct fb_info *info, u8 *dst,
0389 struct fb_image *image, int rotate)
0390 {
0391 u32 tmp;
0392
0393 if (rotate == FB_ROTATE_UD) {
0394 fb_rotate_logo_ud(image->data, dst, image->width,
0395 image->height);
0396 image->dx = info->var.xres - image->width - image->dx;
0397 image->dy = info->var.yres - image->height - image->dy;
0398 } else if (rotate == FB_ROTATE_CW) {
0399 fb_rotate_logo_cw(image->data, dst, image->width,
0400 image->height);
0401 swap(image->width, image->height);
0402 tmp = image->dy;
0403 image->dy = image->dx;
0404 image->dx = info->var.xres - image->width - tmp;
0405 } else if (rotate == FB_ROTATE_CCW) {
0406 fb_rotate_logo_ccw(image->data, dst, image->width,
0407 image->height);
0408 swap(image->width, image->height);
0409 tmp = image->dx;
0410 image->dx = image->dy;
0411 image->dy = info->var.yres - image->height - tmp;
0412 }
0413
0414 image->data = dst;
0415 }
0416
0417 static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
0418 int rotate, unsigned int num)
0419 {
0420 unsigned int x;
0421
0422 if (image->width > info->var.xres || image->height > info->var.yres)
0423 return;
0424
0425 if (rotate == FB_ROTATE_UR) {
0426 for (x = 0;
0427 x < num && image->dx + image->width <= info->var.xres;
0428 x++) {
0429 info->fbops->fb_imageblit(info, image);
0430 image->dx += image->width + 8;
0431 }
0432 } else if (rotate == FB_ROTATE_UD) {
0433 u32 dx = image->dx;
0434
0435 for (x = 0; x < num && image->dx <= dx; x++) {
0436 info->fbops->fb_imageblit(info, image);
0437 image->dx -= image->width + 8;
0438 }
0439 } else if (rotate == FB_ROTATE_CW) {
0440 for (x = 0;
0441 x < num && image->dy + image->height <= info->var.yres;
0442 x++) {
0443 info->fbops->fb_imageblit(info, image);
0444 image->dy += image->height + 8;
0445 }
0446 } else if (rotate == FB_ROTATE_CCW) {
0447 u32 dy = image->dy;
0448
0449 for (x = 0; x < num && image->dy <= dy; x++) {
0450 info->fbops->fb_imageblit(info, image);
0451 image->dy -= image->height + 8;
0452 }
0453 }
0454 }
0455
0456 static int fb_show_logo_line(struct fb_info *info, int rotate,
0457 const struct linux_logo *logo, int y,
0458 unsigned int n)
0459 {
0460 u32 *palette = NULL, *saved_pseudo_palette = NULL;
0461 unsigned char *logo_new = NULL, *logo_rotate = NULL;
0462 struct fb_image image;
0463
0464
0465 if (logo == NULL || info->state != FBINFO_STATE_RUNNING ||
0466 info->fbops->owner)
0467 return 0;
0468
0469 image.depth = 8;
0470 image.data = logo->data;
0471
0472 if (fb_logo.needs_cmapreset)
0473 fb_set_logocmap(info, logo);
0474
0475 if (fb_logo.needs_truepalette ||
0476 fb_logo.needs_directpalette) {
0477 palette = kmalloc(256 * 4, GFP_KERNEL);
0478 if (palette == NULL)
0479 return 0;
0480
0481 if (fb_logo.needs_truepalette)
0482 fb_set_logo_truepalette(info, logo, palette);
0483 else
0484 fb_set_logo_directpalette(info, logo, palette);
0485
0486 saved_pseudo_palette = info->pseudo_palette;
0487 info->pseudo_palette = palette;
0488 }
0489
0490 if (fb_logo.depth <= 4) {
0491 logo_new = kmalloc_array(logo->width, logo->height,
0492 GFP_KERNEL);
0493 if (logo_new == NULL) {
0494 kfree(palette);
0495 if (saved_pseudo_palette)
0496 info->pseudo_palette = saved_pseudo_palette;
0497 return 0;
0498 }
0499 image.data = logo_new;
0500 fb_set_logo(info, logo, logo_new, fb_logo.depth);
0501 }
0502
0503 if (fb_center_logo) {
0504 int xres = info->var.xres;
0505 int yres = info->var.yres;
0506
0507 if (rotate == FB_ROTATE_CW || rotate == FB_ROTATE_CCW) {
0508 xres = info->var.yres;
0509 yres = info->var.xres;
0510 }
0511
0512 while (n && (n * (logo->width + 8) - 8 > xres))
0513 --n;
0514 image.dx = (xres - (n * (logo->width + 8) - 8)) / 2;
0515 image.dy = y ?: (yres - logo->height) / 2;
0516 } else {
0517 image.dx = 0;
0518 image.dy = y;
0519 }
0520
0521 image.width = logo->width;
0522 image.height = logo->height;
0523
0524 if (rotate) {
0525 logo_rotate = kmalloc_array(logo->width, logo->height,
0526 GFP_KERNEL);
0527 if (logo_rotate)
0528 fb_rotate_logo(info, logo_rotate, &image, rotate);
0529 }
0530
0531 fb_do_show_logo(info, &image, rotate, n);
0532
0533 kfree(palette);
0534 if (saved_pseudo_palette != NULL)
0535 info->pseudo_palette = saved_pseudo_palette;
0536 kfree(logo_new);
0537 kfree(logo_rotate);
0538 return image.dy + logo->height;
0539 }
0540
0541
0542 #ifdef CONFIG_FB_LOGO_EXTRA
0543
0544 #define FB_LOGO_EX_NUM_MAX 10
0545 static struct logo_data_extra {
0546 const struct linux_logo *logo;
0547 unsigned int n;
0548 } fb_logo_ex[FB_LOGO_EX_NUM_MAX];
0549 static unsigned int fb_logo_ex_num;
0550
0551 void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n)
0552 {
0553 if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX)
0554 return;
0555
0556 fb_logo_ex[fb_logo_ex_num].logo = logo;
0557 fb_logo_ex[fb_logo_ex_num].n = n;
0558 fb_logo_ex_num++;
0559 }
0560
0561 static int fb_prepare_extra_logos(struct fb_info *info, unsigned int height,
0562 unsigned int yres)
0563 {
0564 unsigned int i;
0565
0566
0567 if (info->fix.visual != FB_VISUAL_TRUECOLOR)
0568 fb_logo_ex_num = 0;
0569
0570 for (i = 0; i < fb_logo_ex_num; i++) {
0571 if (fb_logo_ex[i].logo->type != fb_logo.logo->type) {
0572 fb_logo_ex[i].logo = NULL;
0573 continue;
0574 }
0575 height += fb_logo_ex[i].logo->height;
0576 if (height > yres) {
0577 height -= fb_logo_ex[i].logo->height;
0578 fb_logo_ex_num = i;
0579 break;
0580 }
0581 }
0582 return height;
0583 }
0584
0585 static int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
0586 {
0587 unsigned int i;
0588
0589 for (i = 0; i < fb_logo_ex_num; i++)
0590 y = fb_show_logo_line(info, rotate,
0591 fb_logo_ex[i].logo, y, fb_logo_ex[i].n);
0592
0593 return y;
0594 }
0595
0596 #else
0597
0598 static inline int fb_prepare_extra_logos(struct fb_info *info,
0599 unsigned int height,
0600 unsigned int yres)
0601 {
0602 return height;
0603 }
0604
0605 static inline int fb_show_extra_logos(struct fb_info *info, int y, int rotate)
0606 {
0607 return y;
0608 }
0609
0610 #endif
0611
0612
0613 int fb_prepare_logo(struct fb_info *info, int rotate)
0614 {
0615 int depth = fb_get_color_depth(&info->var, &info->fix);
0616 unsigned int yres;
0617 int height;
0618
0619 memset(&fb_logo, 0, sizeof(struct logo_data));
0620
0621 if (info->flags & FBINFO_MISC_TILEBLITTING ||
0622 info->fbops->owner || !fb_logo_count)
0623 return 0;
0624
0625 if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
0626 depth = info->var.blue.length;
0627 if (info->var.red.length < depth)
0628 depth = info->var.red.length;
0629 if (info->var.green.length < depth)
0630 depth = info->var.green.length;
0631 }
0632
0633 if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
0634
0635 depth = 4;
0636 }
0637
0638
0639 fb_logo.logo = fb_find_logo(depth);
0640
0641 if (!fb_logo.logo) {
0642 return 0;
0643 }
0644
0645 if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD)
0646 yres = info->var.yres;
0647 else
0648 yres = info->var.xres;
0649
0650 if (fb_logo.logo->height > yres) {
0651 fb_logo.logo = NULL;
0652 return 0;
0653 }
0654
0655
0656 if (fb_logo.logo->type == LINUX_LOGO_CLUT224)
0657 fb_logo.depth = 8;
0658 else if (fb_logo.logo->type == LINUX_LOGO_VGA16)
0659 fb_logo.depth = 4;
0660 else
0661 fb_logo.depth = 1;
0662
0663
0664 if (fb_logo.depth > 4 && depth > 4) {
0665 switch (info->fix.visual) {
0666 case FB_VISUAL_TRUECOLOR:
0667 fb_logo.needs_truepalette = 1;
0668 break;
0669 case FB_VISUAL_DIRECTCOLOR:
0670 fb_logo.needs_directpalette = 1;
0671 fb_logo.needs_cmapreset = 1;
0672 break;
0673 case FB_VISUAL_PSEUDOCOLOR:
0674 fb_logo.needs_cmapreset = 1;
0675 break;
0676 }
0677 }
0678
0679 height = fb_logo.logo->height;
0680 if (fb_center_logo)
0681 height += (yres - fb_logo.logo->height) / 2;
0682
0683 return fb_prepare_extra_logos(info, height, yres);
0684 }
0685
0686 int fb_show_logo(struct fb_info *info, int rotate)
0687 {
0688 unsigned int count;
0689 int y;
0690
0691 if (!fb_logo_count)
0692 return 0;
0693
0694 count = fb_logo_count < 0 ? num_online_cpus() : fb_logo_count;
0695 y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, count);
0696 y = fb_show_extra_logos(info, y, rotate);
0697
0698 return y;
0699 }
0700 #else
0701 int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; }
0702 int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
0703 #endif
0704 EXPORT_SYMBOL(fb_prepare_logo);
0705 EXPORT_SYMBOL(fb_show_logo);
0706
0707 static void *fb_seq_start(struct seq_file *m, loff_t *pos)
0708 {
0709 mutex_lock(®istration_lock);
0710 return (*pos < FB_MAX) ? pos : NULL;
0711 }
0712
0713 static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
0714 {
0715 (*pos)++;
0716 return (*pos < FB_MAX) ? pos : NULL;
0717 }
0718
0719 static void fb_seq_stop(struct seq_file *m, void *v)
0720 {
0721 mutex_unlock(®istration_lock);
0722 }
0723
0724 static int fb_seq_show(struct seq_file *m, void *v)
0725 {
0726 int i = *(loff_t *)v;
0727 struct fb_info *fi = registered_fb[i];
0728
0729 if (fi)
0730 seq_printf(m, "%d %s\n", fi->node, fi->fix.id);
0731 return 0;
0732 }
0733
0734 static const struct seq_operations __maybe_unused proc_fb_seq_ops = {
0735 .start = fb_seq_start,
0736 .next = fb_seq_next,
0737 .stop = fb_seq_stop,
0738 .show = fb_seq_show,
0739 };
0740
0741
0742
0743
0744
0745
0746
0747
0748
0749 static struct fb_info *file_fb_info(struct file *file)
0750 {
0751 struct inode *inode = file_inode(file);
0752 int fbidx = iminor(inode);
0753 struct fb_info *info = registered_fb[fbidx];
0754
0755 if (info != file->private_data)
0756 info = NULL;
0757 return info;
0758 }
0759
0760 static ssize_t
0761 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
0762 {
0763 unsigned long p = *ppos;
0764 struct fb_info *info = file_fb_info(file);
0765 u8 *buffer, *dst;
0766 u8 __iomem *src;
0767 int c, cnt = 0, err = 0;
0768 unsigned long total_size;
0769
0770 if (!info || ! info->screen_base)
0771 return -ENODEV;
0772
0773 if (info->state != FBINFO_STATE_RUNNING)
0774 return -EPERM;
0775
0776 if (info->fbops->fb_read)
0777 return info->fbops->fb_read(info, buf, count, ppos);
0778
0779 total_size = info->screen_size;
0780
0781 if (total_size == 0)
0782 total_size = info->fix.smem_len;
0783
0784 if (p >= total_size)
0785 return 0;
0786
0787 if (count >= total_size)
0788 count = total_size;
0789
0790 if (count + p > total_size)
0791 count = total_size - p;
0792
0793 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
0794 GFP_KERNEL);
0795 if (!buffer)
0796 return -ENOMEM;
0797
0798 src = (u8 __iomem *) (info->screen_base + p);
0799
0800 if (info->fbops->fb_sync)
0801 info->fbops->fb_sync(info);
0802
0803 while (count) {
0804 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
0805 dst = buffer;
0806 fb_memcpy_fromfb(dst, src, c);
0807 dst += c;
0808 src += c;
0809
0810 if (copy_to_user(buf, buffer, c)) {
0811 err = -EFAULT;
0812 break;
0813 }
0814 *ppos += c;
0815 buf += c;
0816 cnt += c;
0817 count -= c;
0818 }
0819
0820 kfree(buffer);
0821
0822 return (err) ? err : cnt;
0823 }
0824
0825 static ssize_t
0826 fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
0827 {
0828 unsigned long p = *ppos;
0829 struct fb_info *info = file_fb_info(file);
0830 u8 *buffer, *src;
0831 u8 __iomem *dst;
0832 int c, cnt = 0, err = 0;
0833 unsigned long total_size;
0834
0835 if (!info || !info->screen_base)
0836 return -ENODEV;
0837
0838 if (info->state != FBINFO_STATE_RUNNING)
0839 return -EPERM;
0840
0841 if (info->fbops->fb_write)
0842 return info->fbops->fb_write(info, buf, count, ppos);
0843
0844 total_size = info->screen_size;
0845
0846 if (total_size == 0)
0847 total_size = info->fix.smem_len;
0848
0849 if (p > total_size)
0850 return -EFBIG;
0851
0852 if (count > total_size) {
0853 err = -EFBIG;
0854 count = total_size;
0855 }
0856
0857 if (count + p > total_size) {
0858 if (!err)
0859 err = -ENOSPC;
0860
0861 count = total_size - p;
0862 }
0863
0864 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
0865 GFP_KERNEL);
0866 if (!buffer)
0867 return -ENOMEM;
0868
0869 dst = (u8 __iomem *) (info->screen_base + p);
0870
0871 if (info->fbops->fb_sync)
0872 info->fbops->fb_sync(info);
0873
0874 while (count) {
0875 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
0876 src = buffer;
0877
0878 if (copy_from_user(src, buf, c)) {
0879 err = -EFAULT;
0880 break;
0881 }
0882
0883 fb_memcpy_tofb(dst, src, c);
0884 dst += c;
0885 src += c;
0886 *ppos += c;
0887 buf += c;
0888 cnt += c;
0889 count -= c;
0890 }
0891
0892 kfree(buffer);
0893
0894 return (cnt) ? cnt : err;
0895 }
0896
0897 int
0898 fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
0899 {
0900 struct fb_fix_screeninfo *fix = &info->fix;
0901 unsigned int yres = info->var.yres;
0902 int err = 0;
0903
0904 if (var->yoffset > 0) {
0905 if (var->vmode & FB_VMODE_YWRAP) {
0906 if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
0907 err = -EINVAL;
0908 else
0909 yres = 0;
0910 } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
0911 err = -EINVAL;
0912 }
0913
0914 if (var->xoffset > 0 && (!fix->xpanstep ||
0915 (var->xoffset % fix->xpanstep)))
0916 err = -EINVAL;
0917
0918 if (err || !info->fbops->fb_pan_display ||
0919 var->yoffset > info->var.yres_virtual - yres ||
0920 var->xoffset > info->var.xres_virtual - info->var.xres)
0921 return -EINVAL;
0922
0923 if ((err = info->fbops->fb_pan_display(var, info)))
0924 return err;
0925 info->var.xoffset = var->xoffset;
0926 info->var.yoffset = var->yoffset;
0927 if (var->vmode & FB_VMODE_YWRAP)
0928 info->var.vmode |= FB_VMODE_YWRAP;
0929 else
0930 info->var.vmode &= ~FB_VMODE_YWRAP;
0931 return 0;
0932 }
0933 EXPORT_SYMBOL(fb_pan_display);
0934
0935 static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
0936 u32 activate)
0937 {
0938 struct fb_blit_caps caps, fbcaps;
0939 int err = 0;
0940
0941 memset(&caps, 0, sizeof(caps));
0942 memset(&fbcaps, 0, sizeof(fbcaps));
0943 caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
0944 fbcon_get_requirement(info, &caps);
0945 info->fbops->fb_get_caps(info, &fbcaps, var);
0946
0947 if (((fbcaps.x ^ caps.x) & caps.x) ||
0948 ((fbcaps.y ^ caps.y) & caps.y) ||
0949 (fbcaps.len < caps.len))
0950 err = -EINVAL;
0951
0952 return err;
0953 }
0954
0955 int
0956 fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
0957 {
0958 int ret = 0;
0959 u32 activate;
0960 struct fb_var_screeninfo old_var;
0961 struct fb_videomode mode;
0962 struct fb_event event;
0963 u32 unused;
0964
0965 if (var->activate & FB_ACTIVATE_INV_MODE) {
0966 struct fb_videomode mode1, mode2;
0967
0968 fb_var_to_videomode(&mode1, var);
0969 fb_var_to_videomode(&mode2, &info->var);
0970
0971 ret = fb_mode_is_equal(&mode1, &mode2);
0972 if (!ret) {
0973 ret = fbcon_mode_deleted(info, &mode1);
0974 if (!ret)
0975 fb_delete_videomode(&mode1, &info->modelist);
0976 }
0977
0978 return ret ? -EINVAL : 0;
0979 }
0980
0981 if (!(var->activate & FB_ACTIVATE_FORCE) &&
0982 !memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
0983 return 0;
0984
0985 activate = var->activate;
0986
0987
0988
0989
0990 if ((info->fix.capabilities & FB_CAP_FOURCC) &&
0991 var->grayscale > 1) {
0992 if (var->red.offset || var->green.offset ||
0993 var->blue.offset || var->transp.offset ||
0994 var->red.length || var->green.length ||
0995 var->blue.length || var->transp.length ||
0996 var->red.msb_right || var->green.msb_right ||
0997 var->blue.msb_right || var->transp.msb_right)
0998 return -EINVAL;
0999 }
1000
1001 if (!info->fbops->fb_check_var) {
1002 *var = info->var;
1003 return 0;
1004 }
1005
1006
1007 if (var->xres < 8 || var->yres < 8)
1008 return -EINVAL;
1009
1010
1011 if (check_mul_overflow(var->xres, var->yres, &unused) ||
1012 check_mul_overflow(var->xres_virtual, var->yres_virtual, &unused))
1013 return -EINVAL;
1014
1015 ret = info->fbops->fb_check_var(var, info);
1016
1017 if (ret)
1018 return ret;
1019
1020
1021 if (var->xres_virtual < var->xres ||
1022 var->yres_virtual < var->yres) {
1023 pr_warn("WARNING: fbcon: Driver '%s' missed to adjust virtual screen size (%ux%u vs. %ux%u)\n",
1024 info->fix.id,
1025 var->xres_virtual, var->yres_virtual,
1026 var->xres, var->yres);
1027 return -EINVAL;
1028 }
1029
1030 if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
1031 return 0;
1032
1033 if (info->fbops->fb_get_caps) {
1034 ret = fb_check_caps(info, var, activate);
1035
1036 if (ret)
1037 return ret;
1038 }
1039
1040 old_var = info->var;
1041 info->var = *var;
1042
1043 if (info->fbops->fb_set_par) {
1044 ret = info->fbops->fb_set_par(info);
1045
1046 if (ret) {
1047 info->var = old_var;
1048 printk(KERN_WARNING "detected "
1049 "fb_set_par error, "
1050 "error code: %d\n", ret);
1051 return ret;
1052 }
1053 }
1054
1055 fb_pan_display(info, &info->var);
1056 fb_set_cmap(&info->cmap, info);
1057 fb_var_to_videomode(&mode, &info->var);
1058
1059 if (info->modelist.prev && info->modelist.next &&
1060 !list_empty(&info->modelist))
1061 ret = fb_add_videomode(&mode, &info->modelist);
1062
1063 if (ret)
1064 return ret;
1065
1066 event.info = info;
1067 event.data = &mode;
1068 fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event);
1069
1070 return 0;
1071 }
1072 EXPORT_SYMBOL(fb_set_var);
1073
1074 int
1075 fb_blank(struct fb_info *info, int blank)
1076 {
1077 struct fb_event event;
1078 int ret = -EINVAL;
1079
1080 if (blank > FB_BLANK_POWERDOWN)
1081 blank = FB_BLANK_POWERDOWN;
1082
1083 event.info = info;
1084 event.data = ␣
1085
1086 if (info->fbops->fb_blank)
1087 ret = info->fbops->fb_blank(blank, info);
1088
1089 if (!ret)
1090 fb_notifier_call_chain(FB_EVENT_BLANK, &event);
1091
1092 return ret;
1093 }
1094 EXPORT_SYMBOL(fb_blank);
1095
1096 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
1097 unsigned long arg)
1098 {
1099 const struct fb_ops *fb;
1100 struct fb_var_screeninfo var;
1101 struct fb_fix_screeninfo fix;
1102 struct fb_cmap cmap_from;
1103 struct fb_cmap_user cmap;
1104 void __user *argp = (void __user *)arg;
1105 long ret = 0;
1106
1107 switch (cmd) {
1108 case FBIOGET_VSCREENINFO:
1109 lock_fb_info(info);
1110 var = info->var;
1111 unlock_fb_info(info);
1112
1113 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
1114 break;
1115 case FBIOPUT_VSCREENINFO:
1116 if (copy_from_user(&var, argp, sizeof(var)))
1117 return -EFAULT;
1118 console_lock();
1119 lock_fb_info(info);
1120 ret = fbcon_modechange_possible(info, &var);
1121 if (!ret)
1122 ret = fb_set_var(info, &var);
1123 if (!ret)
1124 fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
1125 unlock_fb_info(info);
1126 console_unlock();
1127 if (!ret && copy_to_user(argp, &var, sizeof(var)))
1128 ret = -EFAULT;
1129 break;
1130 case FBIOGET_FSCREENINFO:
1131 lock_fb_info(info);
1132 memcpy(&fix, &info->fix, sizeof(fix));
1133 if (info->flags & FBINFO_HIDE_SMEM_START)
1134 fix.smem_start = 0;
1135 unlock_fb_info(info);
1136
1137 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
1138 break;
1139 case FBIOPUTCMAP:
1140 if (copy_from_user(&cmap, argp, sizeof(cmap)))
1141 return -EFAULT;
1142 ret = fb_set_user_cmap(&cmap, info);
1143 break;
1144 case FBIOGETCMAP:
1145 if (copy_from_user(&cmap, argp, sizeof(cmap)))
1146 return -EFAULT;
1147 lock_fb_info(info);
1148 cmap_from = info->cmap;
1149 unlock_fb_info(info);
1150 ret = fb_cmap_to_user(&cmap_from, &cmap);
1151 break;
1152 case FBIOPAN_DISPLAY:
1153 if (copy_from_user(&var, argp, sizeof(var)))
1154 return -EFAULT;
1155 console_lock();
1156 lock_fb_info(info);
1157 ret = fb_pan_display(info, &var);
1158 unlock_fb_info(info);
1159 console_unlock();
1160 if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
1161 return -EFAULT;
1162 break;
1163 case FBIO_CURSOR:
1164 ret = -EINVAL;
1165 break;
1166 case FBIOGET_CON2FBMAP:
1167 ret = fbcon_get_con2fb_map_ioctl(argp);
1168 break;
1169 case FBIOPUT_CON2FBMAP:
1170 ret = fbcon_set_con2fb_map_ioctl(argp);
1171 break;
1172 case FBIOBLANK:
1173 if (arg > FB_BLANK_POWERDOWN)
1174 return -EINVAL;
1175 console_lock();
1176 lock_fb_info(info);
1177 ret = fb_blank(info, arg);
1178
1179 fbcon_fb_blanked(info, arg);
1180 unlock_fb_info(info);
1181 console_unlock();
1182 break;
1183 default:
1184 lock_fb_info(info);
1185 fb = info->fbops;
1186 if (fb->fb_ioctl)
1187 ret = fb->fb_ioctl(info, cmd, arg);
1188 else
1189 ret = -ENOTTY;
1190 unlock_fb_info(info);
1191 }
1192 return ret;
1193 }
1194
1195 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1196 {
1197 struct fb_info *info = file_fb_info(file);
1198
1199 if (!info)
1200 return -ENODEV;
1201 return do_fb_ioctl(info, cmd, arg);
1202 }
1203
1204 #ifdef CONFIG_COMPAT
1205 struct fb_fix_screeninfo32 {
1206 char id[16];
1207 compat_caddr_t smem_start;
1208 u32 smem_len;
1209 u32 type;
1210 u32 type_aux;
1211 u32 visual;
1212 u16 xpanstep;
1213 u16 ypanstep;
1214 u16 ywrapstep;
1215 u32 line_length;
1216 compat_caddr_t mmio_start;
1217 u32 mmio_len;
1218 u32 accel;
1219 u16 reserved[3];
1220 };
1221
1222 struct fb_cmap32 {
1223 u32 start;
1224 u32 len;
1225 compat_caddr_t red;
1226 compat_caddr_t green;
1227 compat_caddr_t blue;
1228 compat_caddr_t transp;
1229 };
1230
1231 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
1232 unsigned long arg)
1233 {
1234 struct fb_cmap32 cmap32;
1235 struct fb_cmap cmap_from;
1236 struct fb_cmap_user cmap;
1237
1238 if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
1239 return -EFAULT;
1240
1241 cmap = (struct fb_cmap_user) {
1242 .start = cmap32.start,
1243 .len = cmap32.len,
1244 .red = compat_ptr(cmap32.red),
1245 .green = compat_ptr(cmap32.green),
1246 .blue = compat_ptr(cmap32.blue),
1247 .transp = compat_ptr(cmap32.transp),
1248 };
1249
1250 if (cmd == FBIOPUTCMAP)
1251 return fb_set_user_cmap(&cmap, info);
1252
1253 lock_fb_info(info);
1254 cmap_from = info->cmap;
1255 unlock_fb_info(info);
1256
1257 return fb_cmap_to_user(&cmap_from, &cmap);
1258 }
1259
1260 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
1261 struct fb_fix_screeninfo32 __user *fix32)
1262 {
1263 __u32 data;
1264 int err;
1265
1266 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
1267
1268 data = (__u32) (unsigned long) fix->smem_start;
1269 err |= put_user(data, &fix32->smem_start);
1270
1271 err |= put_user(fix->smem_len, &fix32->smem_len);
1272 err |= put_user(fix->type, &fix32->type);
1273 err |= put_user(fix->type_aux, &fix32->type_aux);
1274 err |= put_user(fix->visual, &fix32->visual);
1275 err |= put_user(fix->xpanstep, &fix32->xpanstep);
1276 err |= put_user(fix->ypanstep, &fix32->ypanstep);
1277 err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
1278 err |= put_user(fix->line_length, &fix32->line_length);
1279
1280 data = (__u32) (unsigned long) fix->mmio_start;
1281 err |= put_user(data, &fix32->mmio_start);
1282
1283 err |= put_user(fix->mmio_len, &fix32->mmio_len);
1284 err |= put_user(fix->accel, &fix32->accel);
1285 err |= copy_to_user(fix32->reserved, fix->reserved,
1286 sizeof(fix->reserved));
1287
1288 if (err)
1289 return -EFAULT;
1290 return 0;
1291 }
1292
1293 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
1294 unsigned long arg)
1295 {
1296 struct fb_fix_screeninfo fix;
1297
1298 lock_fb_info(info);
1299 fix = info->fix;
1300 if (info->flags & FBINFO_HIDE_SMEM_START)
1301 fix.smem_start = 0;
1302 unlock_fb_info(info);
1303 return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
1304 }
1305
1306 static long fb_compat_ioctl(struct file *file, unsigned int cmd,
1307 unsigned long arg)
1308 {
1309 struct fb_info *info = file_fb_info(file);
1310 const struct fb_ops *fb;
1311 long ret = -ENOIOCTLCMD;
1312
1313 if (!info)
1314 return -ENODEV;
1315 fb = info->fbops;
1316 switch(cmd) {
1317 case FBIOGET_VSCREENINFO:
1318 case FBIOPUT_VSCREENINFO:
1319 case FBIOPAN_DISPLAY:
1320 case FBIOGET_CON2FBMAP:
1321 case FBIOPUT_CON2FBMAP:
1322 arg = (unsigned long) compat_ptr(arg);
1323 fallthrough;
1324 case FBIOBLANK:
1325 ret = do_fb_ioctl(info, cmd, arg);
1326 break;
1327
1328 case FBIOGET_FSCREENINFO:
1329 ret = fb_get_fscreeninfo(info, cmd, arg);
1330 break;
1331
1332 case FBIOGETCMAP:
1333 case FBIOPUTCMAP:
1334 ret = fb_getput_cmap(info, cmd, arg);
1335 break;
1336
1337 default:
1338 if (fb->fb_compat_ioctl)
1339 ret = fb->fb_compat_ioctl(info, cmd, arg);
1340 break;
1341 }
1342 return ret;
1343 }
1344 #endif
1345
1346 static int
1347 fb_mmap(struct file *file, struct vm_area_struct * vma)
1348 {
1349 struct fb_info *info = file_fb_info(file);
1350 unsigned long mmio_pgoff;
1351 unsigned long start;
1352 u32 len;
1353
1354 if (!info)
1355 return -ENODEV;
1356 mutex_lock(&info->mm_lock);
1357
1358 if (info->fbops->fb_mmap) {
1359 int res;
1360
1361
1362
1363
1364
1365 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
1366 res = info->fbops->fb_mmap(info, vma);
1367 mutex_unlock(&info->mm_lock);
1368 return res;
1369 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
1370 } else if (info->fbdefio) {
1371
1372
1373
1374
1375 dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n");
1376 mutex_unlock(&info->mm_lock);
1377 return -ENODEV;
1378 #endif
1379 }
1380
1381
1382
1383
1384
1385 start = info->fix.smem_start;
1386 len = info->fix.smem_len;
1387 mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
1388 if (vma->vm_pgoff >= mmio_pgoff) {
1389 if (info->var.accel_flags) {
1390 mutex_unlock(&info->mm_lock);
1391 return -EINVAL;
1392 }
1393
1394 vma->vm_pgoff -= mmio_pgoff;
1395 start = info->fix.mmio_start;
1396 len = info->fix.mmio_len;
1397 }
1398 mutex_unlock(&info->mm_lock);
1399
1400 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
1401 fb_pgprotect(file, vma, start);
1402
1403 return vm_iomap_memory(vma, start, len);
1404 }
1405
1406 static int
1407 fb_open(struct inode *inode, struct file *file)
1408 __acquires(&info->lock)
1409 __releases(&info->lock)
1410 {
1411 int fbidx = iminor(inode);
1412 struct fb_info *info;
1413 int res = 0;
1414
1415 info = get_fb_info(fbidx);
1416 if (!info) {
1417 request_module("fb%d", fbidx);
1418 info = get_fb_info(fbidx);
1419 if (!info)
1420 return -ENODEV;
1421 }
1422 if (IS_ERR(info))
1423 return PTR_ERR(info);
1424
1425 lock_fb_info(info);
1426 if (!try_module_get(info->fbops->owner)) {
1427 res = -ENODEV;
1428 goto out;
1429 }
1430 file->private_data = info;
1431 if (info->fbops->fb_open) {
1432 res = info->fbops->fb_open(info,1);
1433 if (res)
1434 module_put(info->fbops->owner);
1435 }
1436 #ifdef CONFIG_FB_DEFERRED_IO
1437 if (info->fbdefio)
1438 fb_deferred_io_open(info, inode, file);
1439 #endif
1440 out:
1441 unlock_fb_info(info);
1442 if (res)
1443 put_fb_info(info);
1444 return res;
1445 }
1446
1447 static int
1448 fb_release(struct inode *inode, struct file *file)
1449 __acquires(&info->lock)
1450 __releases(&info->lock)
1451 {
1452 struct fb_info * const info = file->private_data;
1453
1454 lock_fb_info(info);
1455 if (info->fbops->fb_release)
1456 info->fbops->fb_release(info,1);
1457 module_put(info->fbops->owner);
1458 unlock_fb_info(info);
1459 put_fb_info(info);
1460 return 0;
1461 }
1462
1463 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
1464 unsigned long get_fb_unmapped_area(struct file *filp,
1465 unsigned long addr, unsigned long len,
1466 unsigned long pgoff, unsigned long flags)
1467 {
1468 struct fb_info * const info = filp->private_data;
1469 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
1470
1471 if (pgoff > fb_size || len > fb_size - pgoff)
1472 return -EINVAL;
1473
1474 return (unsigned long)info->screen_base + pgoff;
1475 }
1476 #endif
1477
1478 static const struct file_operations fb_fops = {
1479 .owner = THIS_MODULE,
1480 .read = fb_read,
1481 .write = fb_write,
1482 .unlocked_ioctl = fb_ioctl,
1483 #ifdef CONFIG_COMPAT
1484 .compat_ioctl = fb_compat_ioctl,
1485 #endif
1486 .mmap = fb_mmap,
1487 .open = fb_open,
1488 .release = fb_release,
1489 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
1490 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
1491 !defined(CONFIG_MMU))
1492 .get_unmapped_area = get_fb_unmapped_area,
1493 #endif
1494 #ifdef CONFIG_FB_DEFERRED_IO
1495 .fsync = fb_deferred_io_fsync,
1496 #endif
1497 .llseek = default_llseek,
1498 };
1499
1500 struct class *fb_class;
1501 EXPORT_SYMBOL(fb_class);
1502
1503 static int fb_check_foreignness(struct fb_info *fi)
1504 {
1505 const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
1506
1507 fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
1508
1509 #ifdef __BIG_ENDIAN
1510 fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
1511 #else
1512 fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
1513 #endif
1514
1515 if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
1516 pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
1517 "support this framebuffer\n", fi->fix.id);
1518 return -ENOSYS;
1519 } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
1520 pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
1521 "support this framebuffer\n", fi->fix.id);
1522 return -ENOSYS;
1523 }
1524
1525 return 0;
1526 }
1527
1528 static bool apertures_overlap(struct aperture *gen, struct aperture *hw)
1529 {
1530
1531 if (gen->base == hw->base)
1532 return true;
1533
1534 if (gen->base > hw->base && gen->base < hw->base + hw->size)
1535 return true;
1536 return false;
1537 }
1538
1539 static bool fb_do_apertures_overlap(struct apertures_struct *gena,
1540 struct apertures_struct *hwa)
1541 {
1542 int i, j;
1543 if (!hwa || !gena)
1544 return false;
1545
1546 for (i = 0; i < hwa->count; ++i) {
1547 struct aperture *h = &hwa->ranges[i];
1548 for (j = 0; j < gena->count; ++j) {
1549 struct aperture *g = &gena->ranges[j];
1550 printk(KERN_DEBUG "checking generic (%llx %llx) vs hw (%llx %llx)\n",
1551 (unsigned long long)g->base,
1552 (unsigned long long)g->size,
1553 (unsigned long long)h->base,
1554 (unsigned long long)h->size);
1555 if (apertures_overlap(g, h))
1556 return true;
1557 }
1558 }
1559
1560 return false;
1561 }
1562
1563 static void do_unregister_framebuffer(struct fb_info *fb_info);
1564
1565 #define VGA_FB_PHYS 0xA0000
1566 static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
1567 const char *name, bool primary)
1568 {
1569 int i;
1570
1571 restart_removal:
1572
1573 for_each_registered_fb(i) {
1574 struct apertures_struct *gen_aper;
1575 struct device *device;
1576
1577 if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE))
1578 continue;
1579
1580 gen_aper = registered_fb[i]->apertures;
1581 device = registered_fb[i]->device;
1582 if (fb_do_apertures_overlap(gen_aper, a) ||
1583 (primary && gen_aper && gen_aper->count &&
1584 gen_aper->ranges[0].base == VGA_FB_PHYS)) {
1585
1586 printk(KERN_INFO "fb%d: switching to %s from %s\n",
1587 i, name, registered_fb[i]->fix.id);
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600 if (!device) {
1601 pr_warn("fb%d: no device set\n", i);
1602 do_unregister_framebuffer(registered_fb[i]);
1603 } else if (dev_is_platform(device)) {
1604
1605
1606
1607
1608
1609 mutex_unlock(®istration_lock);
1610 platform_device_unregister(to_platform_device(device));
1611 mutex_lock(®istration_lock);
1612 } else {
1613 pr_warn("fb%d: cannot remove device\n", i);
1614 do_unregister_framebuffer(registered_fb[i]);
1615 }
1616
1617
1618
1619
1620 goto restart_removal;
1621 }
1622 }
1623 }
1624
1625 static int do_register_framebuffer(struct fb_info *fb_info)
1626 {
1627 int i;
1628 struct fb_videomode mode;
1629
1630 if (fb_check_foreignness(fb_info))
1631 return -ENOSYS;
1632
1633 do_remove_conflicting_framebuffers(fb_info->apertures,
1634 fb_info->fix.id,
1635 fb_is_primary_device(fb_info));
1636
1637 if (num_registered_fb == FB_MAX)
1638 return -ENXIO;
1639
1640 num_registered_fb++;
1641 for (i = 0 ; i < FB_MAX; i++)
1642 if (!registered_fb[i])
1643 break;
1644 fb_info->node = i;
1645 refcount_set(&fb_info->count, 1);
1646 mutex_init(&fb_info->lock);
1647 mutex_init(&fb_info->mm_lock);
1648
1649 fb_info->dev = device_create(fb_class, fb_info->device,
1650 MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
1651 if (IS_ERR(fb_info->dev)) {
1652
1653 printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
1654 fb_info->dev = NULL;
1655 } else
1656 fb_init_device(fb_info);
1657
1658 if (fb_info->pixmap.addr == NULL) {
1659 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
1660 if (fb_info->pixmap.addr) {
1661 fb_info->pixmap.size = FBPIXMAPSIZE;
1662 fb_info->pixmap.buf_align = 1;
1663 fb_info->pixmap.scan_align = 1;
1664 fb_info->pixmap.access_align = 32;
1665 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
1666 }
1667 }
1668 fb_info->pixmap.offset = 0;
1669
1670 if (!fb_info->pixmap.blit_x)
1671 fb_info->pixmap.blit_x = ~(u32)0;
1672
1673 if (!fb_info->pixmap.blit_y)
1674 fb_info->pixmap.blit_y = ~(u32)0;
1675
1676 if (!fb_info->modelist.prev || !fb_info->modelist.next)
1677 INIT_LIST_HEAD(&fb_info->modelist);
1678
1679 if (fb_info->skip_vt_switch)
1680 pm_vt_switch_required(fb_info->dev, false);
1681 else
1682 pm_vt_switch_required(fb_info->dev, true);
1683
1684 fb_var_to_videomode(&mode, &fb_info->var);
1685 fb_add_videomode(&mode, &fb_info->modelist);
1686 registered_fb[i] = fb_info;
1687
1688 #ifdef CONFIG_GUMSTIX_AM200EPD
1689 {
1690 struct fb_event event;
1691 event.info = fb_info;
1692 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
1693 }
1694 #endif
1695
1696 return fbcon_fb_registered(fb_info);
1697 }
1698
1699 static void unbind_console(struct fb_info *fb_info)
1700 {
1701 int i = fb_info->node;
1702
1703 if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
1704 return;
1705
1706 fbcon_fb_unbind(fb_info);
1707 }
1708
1709 static void unlink_framebuffer(struct fb_info *fb_info)
1710 {
1711 int i;
1712
1713 i = fb_info->node;
1714 if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
1715 return;
1716
1717 if (!fb_info->dev)
1718 return;
1719
1720 device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1721
1722 pm_vt_switch_unregister(fb_info->dev);
1723
1724 unbind_console(fb_info);
1725
1726 fb_info->dev = NULL;
1727 }
1728
1729 static void do_unregister_framebuffer(struct fb_info *fb_info)
1730 {
1731 unlink_framebuffer(fb_info);
1732 if (fb_info->pixmap.addr &&
1733 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) {
1734 kfree(fb_info->pixmap.addr);
1735 fb_info->pixmap.addr = NULL;
1736 }
1737
1738 fb_destroy_modelist(&fb_info->modelist);
1739 registered_fb[fb_info->node] = NULL;
1740 num_registered_fb--;
1741 fb_cleanup_device(fb_info);
1742 #ifdef CONFIG_GUMSTIX_AM200EPD
1743 {
1744 struct fb_event event;
1745 event.info = fb_info;
1746 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1747 }
1748 #endif
1749 fbcon_fb_unregistered(fb_info);
1750
1751
1752 put_fb_info(fb_info);
1753 }
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765 int remove_conflicting_framebuffers(struct apertures_struct *a,
1766 const char *name, bool primary)
1767 {
1768 bool do_free = false;
1769
1770 if (!a) {
1771 a = alloc_apertures(1);
1772 if (!a)
1773 return -ENOMEM;
1774
1775 a->ranges[0].base = 0;
1776 a->ranges[0].size = ~0;
1777 do_free = true;
1778 }
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789 sysfb_disable();
1790
1791 mutex_lock(®istration_lock);
1792 do_remove_conflicting_framebuffers(a, name, primary);
1793 mutex_unlock(®istration_lock);
1794
1795 if (do_free)
1796 kfree(a);
1797
1798 return 0;
1799 }
1800 EXPORT_SYMBOL(remove_conflicting_framebuffers);
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813 int remove_conflicting_pci_framebuffers(struct pci_dev *pdev, const char *name)
1814 {
1815 struct apertures_struct *ap;
1816 bool primary = false;
1817 int err, idx, bar;
1818
1819 for (idx = 0, bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
1820 if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
1821 continue;
1822 idx++;
1823 }
1824
1825 ap = alloc_apertures(idx);
1826 if (!ap)
1827 return -ENOMEM;
1828
1829 for (idx = 0, bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
1830 if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
1831 continue;
1832 ap->ranges[idx].base = pci_resource_start(pdev, bar);
1833 ap->ranges[idx].size = pci_resource_len(pdev, bar);
1834 pci_dbg(pdev, "%s: bar %d: 0x%lx -> 0x%lx\n", __func__, bar,
1835 (unsigned long)pci_resource_start(pdev, bar),
1836 (unsigned long)pci_resource_end(pdev, bar));
1837 idx++;
1838 }
1839
1840 #ifdef CONFIG_X86
1841 primary = pdev->resource[PCI_ROM_RESOURCE].flags &
1842 IORESOURCE_ROM_SHADOW;
1843 #endif
1844 err = remove_conflicting_framebuffers(ap, name, primary);
1845 kfree(ap);
1846 return err;
1847 }
1848 EXPORT_SYMBOL(remove_conflicting_pci_framebuffers);
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859 int
1860 register_framebuffer(struct fb_info *fb_info)
1861 {
1862 int ret;
1863
1864 mutex_lock(®istration_lock);
1865 ret = do_register_framebuffer(fb_info);
1866 mutex_unlock(®istration_lock);
1867
1868 return ret;
1869 }
1870 EXPORT_SYMBOL(register_framebuffer);
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888 void
1889 unregister_framebuffer(struct fb_info *fb_info)
1890 {
1891 mutex_lock(®istration_lock);
1892 do_unregister_framebuffer(fb_info);
1893 mutex_unlock(®istration_lock);
1894 }
1895 EXPORT_SYMBOL(unregister_framebuffer);
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906 void fb_set_suspend(struct fb_info *info, int state)
1907 {
1908 WARN_CONSOLE_UNLOCKED();
1909
1910 if (state) {
1911 fbcon_suspended(info);
1912 info->state = FBINFO_STATE_SUSPENDED;
1913 } else {
1914 info->state = FBINFO_STATE_RUNNING;
1915 fbcon_resumed(info);
1916 }
1917 }
1918 EXPORT_SYMBOL(fb_set_suspend);
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929 static int __init
1930 fbmem_init(void)
1931 {
1932 int ret;
1933
1934 if (!proc_create_seq("fb", 0, NULL, &proc_fb_seq_ops))
1935 return -ENOMEM;
1936
1937 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
1938 if (ret) {
1939 printk("unable to get major %d for fb devs\n", FB_MAJOR);
1940 goto err_chrdev;
1941 }
1942
1943 fb_class = class_create(THIS_MODULE, "graphics");
1944 if (IS_ERR(fb_class)) {
1945 ret = PTR_ERR(fb_class);
1946 pr_warn("Unable to create fb class; errno = %d\n", ret);
1947 fb_class = NULL;
1948 goto err_class;
1949 }
1950
1951 fb_console_init();
1952
1953 return 0;
1954
1955 err_class:
1956 unregister_chrdev(FB_MAJOR, "fb");
1957 err_chrdev:
1958 remove_proc_entry("fb", NULL);
1959 return ret;
1960 }
1961
1962 #ifdef MODULE
1963 module_init(fbmem_init);
1964 static void __exit
1965 fbmem_exit(void)
1966 {
1967 fb_console_exit();
1968
1969 remove_proc_entry("fb", NULL);
1970 class_destroy(fb_class);
1971 unregister_chrdev(FB_MAJOR, "fb");
1972 }
1973
1974 module_exit(fbmem_exit);
1975 MODULE_LICENSE("GPL");
1976 MODULE_DESCRIPTION("Framebuffer base");
1977 #else
1978 subsys_initcall(fbmem_init);
1979 #endif
1980
1981 int fb_new_modelist(struct fb_info *info)
1982 {
1983 struct fb_var_screeninfo var = info->var;
1984 struct list_head *pos, *n;
1985 struct fb_modelist *modelist;
1986 struct fb_videomode *m, mode;
1987 int err;
1988
1989 list_for_each_safe(pos, n, &info->modelist) {
1990 modelist = list_entry(pos, struct fb_modelist, list);
1991 m = &modelist->mode;
1992 fb_videomode_to_var(&var, m);
1993 var.activate = FB_ACTIVATE_TEST;
1994 err = fb_set_var(info, &var);
1995 fb_var_to_videomode(&mode, &var);
1996 if (err || !fb_mode_is_equal(m, &mode)) {
1997 list_del(pos);
1998 kfree(pos);
1999 }
2000 }
2001
2002 if (list_empty(&info->modelist))
2003 return 1;
2004
2005 fbcon_new_modelist(info);
2006
2007 return 0;
2008 }
2009
2010 MODULE_LICENSE("GPL");