static int efifb_probe()

in fbdev/efifb.c [344:617]


static int efifb_probe(struct platform_device *dev)
{
	struct fb_info *info;
	int err, orientation;
	unsigned int size_vmode;
	unsigned int size_remap;
	unsigned int size_total;
	char *option = NULL;
	efi_memory_desc_t md;

	/*
	 * Generic drivers must not be registered if a framebuffer exists.
	 * If a native driver was probed, the display hardware was already
	 * taken and attempting to use the system framebuffer is dangerous.
	 */
	if (num_registered_fb > 0) {
		dev_err(&dev->dev,
			"efifb: a framebuffer is already registered\n");
		return -EINVAL;
	}

	if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
		return -ENODEV;

	if (fb_get_options("efifb", &option))
		return -ENODEV;
	efifb_setup(option);

	/* We don't get linelength from UGA Draw Protocol, only from
	 * EFI Graphics Protocol.  So if it's not in DMI, and it's not
	 * passed in from the user, we really can't use the framebuffer.
	 */
	if (!screen_info.lfb_linelength)
		return -ENODEV;

	if (!screen_info.lfb_depth)
		screen_info.lfb_depth = 32;
	if (!screen_info.pages)
		screen_info.pages = 1;
	if (!fb_base_is_valid()) {
		printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
		return -ENODEV;
	}
	printk(KERN_INFO "efifb: probing for efifb\n");

	/* just assume they're all unset if any are */
	if (!screen_info.blue_size) {
		screen_info.blue_size = 8;
		screen_info.blue_pos = 0;
		screen_info.green_size = 8;
		screen_info.green_pos = 8;
		screen_info.red_size = 8;
		screen_info.red_pos = 16;
		screen_info.rsvd_size = 8;
		screen_info.rsvd_pos = 24;
	}

	efifb_fix.smem_start = screen_info.lfb_base;

	if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
		u64 ext_lfb_base;

		ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
		efifb_fix.smem_start |= ext_lfb_base;
	}

	if (bar_resource &&
	    bar_resource->start + bar_offset != efifb_fix.smem_start) {
		dev_info(&efifb_pci_dev->dev,
			 "BAR has moved, updating efifb address\n");
		efifb_fix.smem_start = bar_resource->start + bar_offset;
	}

	efifb_defined.bits_per_pixel = screen_info.lfb_depth;
	efifb_defined.xres = screen_info.lfb_width;
	efifb_defined.yres = screen_info.lfb_height;
	efifb_fix.line_length = screen_info.lfb_linelength;

	/*   size_vmode -- that is the amount of memory needed for the
	 *                 used video mode, i.e. the minimum amount of
	 *                 memory we need. */
	size_vmode = efifb_defined.yres * efifb_fix.line_length;

	/*   size_total -- all video memory we have. Used for
	 *                 entries, ressource allocation and bounds
	 *                 checking. */
	size_total = screen_info.lfb_size;
	if (size_total < size_vmode)
		size_total = size_vmode;

	/*   size_remap -- the amount of video memory we are going to
	 *                 use for efifb.  With modern cards it is no
	 *                 option to simply use size_total as that
	 *                 wastes plenty of kernel address space. */
	size_remap  = size_vmode * 2;
	if (size_remap > size_total)
		size_remap = size_total;
	if (size_remap % PAGE_SIZE)
		size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
	efifb_fix.smem_len = size_remap;

	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
		request_mem_succeeded = true;
	} else {
		/* We cannot make this fatal. Sometimes this comes from magic
		   spaces our resource handlers simply don't know about */
		pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
			efifb_fix.smem_start);
	}

	info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
	if (!info) {
		err = -ENOMEM;
		goto err_release_mem;
	}
	platform_set_drvdata(dev, info);
	info->pseudo_palette = info->par;
	info->par = NULL;

	info->apertures = alloc_apertures(1);
	if (!info->apertures) {
		err = -ENOMEM;
		goto err_release_fb;
	}
	info->apertures->ranges[0].base = efifb_fix.smem_start;
	info->apertures->ranges[0].size = size_remap;

	if (efi_enabled(EFI_MEMMAP) &&
	    !efi_mem_desc_lookup(efifb_fix.smem_start, &md)) {
		if ((efifb_fix.smem_start + efifb_fix.smem_len) >
		    (md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT))) {
			pr_err("efifb: video memory @ 0x%lx spans multiple EFI memory regions\n",
			       efifb_fix.smem_start);
			err = -EIO;
			goto err_release_fb;
		}
		/*
		 * If the UEFI memory map covers the efifb region, we may only
		 * remap it using the attributes the memory map prescribes.
		 */
		md.attribute &= EFI_MEMORY_UC | EFI_MEMORY_WC |
				EFI_MEMORY_WT | EFI_MEMORY_WB;
		if (md.attribute) {
			mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
			mem_flags &= md.attribute;
		}
	}
	if (mem_flags & EFI_MEMORY_WC)
		info->screen_base = ioremap_wc(efifb_fix.smem_start,
					       efifb_fix.smem_len);
	else if (mem_flags & EFI_MEMORY_UC)
		info->screen_base = ioremap(efifb_fix.smem_start,
					    efifb_fix.smem_len);
	else if (mem_flags & EFI_MEMORY_WT)
		info->screen_base = memremap(efifb_fix.smem_start,
					     efifb_fix.smem_len, MEMREMAP_WT);
	else if (mem_flags & EFI_MEMORY_WB)
		info->screen_base = memremap(efifb_fix.smem_start,
					     efifb_fix.smem_len, MEMREMAP_WB);
	if (!info->screen_base) {
		pr_err("efifb: abort, cannot remap video memory 0x%x @ 0x%lx\n",
			efifb_fix.smem_len, efifb_fix.smem_start);
		err = -EIO;
		goto err_release_fb;
	}

	efifb_show_boot_graphics(info);

	pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
	       efifb_fix.smem_start, size_remap/1024, size_total/1024);
	pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
	       efifb_defined.xres, efifb_defined.yres,
	       efifb_defined.bits_per_pixel, efifb_fix.line_length,
	       screen_info.pages);

	efifb_defined.xres_virtual = efifb_defined.xres;
	efifb_defined.yres_virtual = efifb_fix.smem_len /
					efifb_fix.line_length;
	pr_info("efifb: scrolling: redraw\n");
	efifb_defined.yres_virtual = efifb_defined.yres;

	/* some dummy values for timing to make fbset happy */
	efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
					1000 / efifb_defined.yres;
	efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
	efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;

	efifb_defined.red.offset    = screen_info.red_pos;
	efifb_defined.red.length    = screen_info.red_size;
	efifb_defined.green.offset  = screen_info.green_pos;
	efifb_defined.green.length  = screen_info.green_size;
	efifb_defined.blue.offset   = screen_info.blue_pos;
	efifb_defined.blue.length   = screen_info.blue_size;
	efifb_defined.transp.offset = screen_info.rsvd_pos;
	efifb_defined.transp.length = screen_info.rsvd_size;

	pr_info("efifb: %s: "
	       "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
	       "Truecolor",
	       screen_info.rsvd_size,
	       screen_info.red_size,
	       screen_info.green_size,
	       screen_info.blue_size,
	       screen_info.rsvd_pos,
	       screen_info.red_pos,
	       screen_info.green_pos,
	       screen_info.blue_pos);

	efifb_fix.ypanstep  = 0;
	efifb_fix.ywrapstep = 0;

	info->fbops = &efifb_ops;
	info->var = efifb_defined;
	info->fix = efifb_fix;
	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;

	orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
						      efifb_defined.yres);
	switch (orientation) {
	default:
		info->fbcon_rotate_hint = FB_ROTATE_UR;
		break;
	case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
		info->fbcon_rotate_hint = FB_ROTATE_UD;
		break;
	case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
		info->fbcon_rotate_hint = FB_ROTATE_CCW;
		break;
	case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
		info->fbcon_rotate_hint = FB_ROTATE_CW;
		break;
	}

	err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
	if (err) {
		pr_err("efifb: cannot add sysfs attrs\n");
		goto err_unmap;
	}
	err = fb_alloc_cmap(&info->cmap, 256, 0);
	if (err < 0) {
		pr_err("efifb: cannot allocate colormap\n");
		goto err_groups;
	}

	if (efifb_pci_dev)
		WARN_ON(pm_runtime_get_sync(&efifb_pci_dev->dev) < 0);

	err = register_framebuffer(info);
	if (err < 0) {
		pr_err("efifb: cannot register framebuffer\n");
		goto err_put_rpm_ref;
	}
	fb_info(info, "%s frame buffer device\n", info->fix.id);
	return 0;

err_put_rpm_ref:
	if (efifb_pci_dev)
		pm_runtime_put(&efifb_pci_dev->dev);

	fb_dealloc_cmap(&info->cmap);
err_groups:
	sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
err_unmap:
	if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
		iounmap(info->screen_base);
	else
		memunmap(info->screen_base);
err_release_fb:
	framebuffer_release(info);
err_release_mem:
	if (request_mem_succeeded)
		release_mem_region(efifb_fix.smem_start, size_total);
	return err;
}