static int pm2fb_probe()

in fbdev/pm2fb.c [1514:1711]


static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct pm2fb_par *default_par;
	struct fb_info *info;
	int err;
	int retval = -ENXIO;

	err = pci_enable_device(pdev);
	if (err) {
		printk(KERN_WARNING "pm2fb: Can't enable pdev: %d\n", err);
		return err;
	}

	info = framebuffer_alloc(sizeof(struct pm2fb_par), &pdev->dev);
	if (!info)
		return -ENOMEM;
	default_par = info->par;

	switch (pdev->device) {
	case  PCI_DEVICE_ID_TI_TVP4020:
		strcpy(pm2fb_fix.id, "TVP4020");
		default_par->type = PM2_TYPE_PERMEDIA2;
		break;
	case  PCI_DEVICE_ID_3DLABS_PERMEDIA2:
		strcpy(pm2fb_fix.id, "Permedia2");
		default_par->type = PM2_TYPE_PERMEDIA2;
		break;
	case  PCI_DEVICE_ID_3DLABS_PERMEDIA2V:
		strcpy(pm2fb_fix.id, "Permedia2v");
		default_par->type = PM2_TYPE_PERMEDIA2V;
		break;
	}

	pm2fb_fix.mmio_start = pci_resource_start(pdev, 0);
	pm2fb_fix.mmio_len = PM2_REGS_SIZE;

#if defined(__BIG_ENDIAN)
	/*
	 * PM2 has a 64k register file, mapped twice in 128k. Lower
	 * map is little-endian, upper map is big-endian.
	 */
	pm2fb_fix.mmio_start += PM2_REGS_SIZE;
	DPRINTK("Adjusting register base for big-endian.\n");
#endif
	DPRINTK("Register base at 0x%lx\n", pm2fb_fix.mmio_start);

	/* Registers - request region and map it. */
	if (!request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len,
				"pm2fb regbase")) {
		printk(KERN_WARNING "pm2fb: Can't reserve regbase.\n");
		goto err_exit_neither;
	}
	default_par->v_regs =
		ioremap(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
	if (!default_par->v_regs) {
		printk(KERN_WARNING "pm2fb: Can't remap %s register area.\n",
		       pm2fb_fix.id);
		release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
		goto err_exit_neither;
	}

	/* Stash away memory register info for use when we reset the board */
	default_par->mem_control = pm2_RD(default_par, PM2R_MEM_CONTROL);
	default_par->boot_address = pm2_RD(default_par, PM2R_BOOT_ADDRESS);
	default_par->mem_config = pm2_RD(default_par, PM2R_MEM_CONFIG);
	DPRINTK("MemControl 0x%x BootAddress 0x%x MemConfig 0x%x\n",
		default_par->mem_control, default_par->boot_address,
		default_par->mem_config);

	if (default_par->mem_control == 0 &&
		default_par->boot_address == 0x31 &&
		default_par->mem_config == 0x259fffff) {
		default_par->memclock = CVPPC_MEMCLOCK;
		default_par->mem_control = 0;
		default_par->boot_address = 0x20;
		default_par->mem_config = 0xe6002021;
		if (pdev->subsystem_vendor == 0x1048 &&
			pdev->subsystem_device == 0x0a31) {
			DPRINTK("subsystem_vendor: %04x, "
				"subsystem_device: %04x\n",
				pdev->subsystem_vendor, pdev->subsystem_device);
			DPRINTK("We have not been initialized by VGA BIOS and "
				"are running on an Elsa Winner 2000 Office\n");
			DPRINTK("Initializing card timings manually...\n");
			default_par->memclock = 100000;
		}
		if (pdev->subsystem_vendor == 0x3d3d &&
			pdev->subsystem_device == 0x0100) {
			DPRINTK("subsystem_vendor: %04x, "
				"subsystem_device: %04x\n",
				pdev->subsystem_vendor, pdev->subsystem_device);
			DPRINTK("We have not been initialized by VGA BIOS and "
				"are running on an 3dlabs reference board\n");
			DPRINTK("Initializing card timings manually...\n");
			default_par->memclock = 74894;
		}
	}

	/* Now work out how big lfb is going to be. */
	switch (default_par->mem_config & PM2F_MEM_CONFIG_RAM_MASK) {
	case PM2F_MEM_BANKS_1:
		pm2fb_fix.smem_len = 0x200000;
		break;
	case PM2F_MEM_BANKS_2:
		pm2fb_fix.smem_len = 0x400000;
		break;
	case PM2F_MEM_BANKS_3:
		pm2fb_fix.smem_len = 0x600000;
		break;
	case PM2F_MEM_BANKS_4:
		pm2fb_fix.smem_len = 0x800000;
		break;
	}
	pm2fb_fix.smem_start = pci_resource_start(pdev, 1);

	/* Linear frame buffer - request region and map it. */
	if (!request_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len,
				"pm2fb smem")) {
		printk(KERN_WARNING "pm2fb: Can't reserve smem.\n");
		goto err_exit_mmio;
	}
	info->screen_base =
		ioremap_wc(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
	if (!info->screen_base) {
		printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n");
		release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
		goto err_exit_mmio;
	}

	if (!nomtrr)
		default_par->wc_cookie = arch_phys_wc_add(pm2fb_fix.smem_start,
							  pm2fb_fix.smem_len);

	info->fbops		= &pm2fb_ops;
	info->fix		= pm2fb_fix;
	info->pseudo_palette	= default_par->palette;
	info->flags		= FBINFO_DEFAULT |
				  FBINFO_HWACCEL_YPAN |
				  FBINFO_HWACCEL_COPYAREA |
				  FBINFO_HWACCEL_IMAGEBLIT |
				  FBINFO_HWACCEL_FILLRECT;

	info->pixmap.addr = kmalloc(PM2_PIXMAP_SIZE, GFP_KERNEL);
	if (!info->pixmap.addr) {
		retval = -ENOMEM;
		goto err_exit_pixmap;
	}
	info->pixmap.size = PM2_PIXMAP_SIZE;
	info->pixmap.buf_align = 4;
	info->pixmap.scan_align = 4;
	info->pixmap.access_align = 32;
	info->pixmap.flags = FB_PIXMAP_SYSTEM;

	if (noaccel) {
		printk(KERN_DEBUG "disabling acceleration\n");
		info->flags |= FBINFO_HWACCEL_DISABLED;
		info->pixmap.scan_align = 1;
	}

	if (!mode_option)
		mode_option = "640x480@60";

	err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
	if (!err || err == 4)
		info->var = pm2fb_var;

	retval = fb_alloc_cmap(&info->cmap, 256, 0);
	if (retval < 0)
		goto err_exit_both;

	retval = register_framebuffer(info);
	if (retval < 0)
		goto err_exit_all;

	fb_info(info, "%s frame buffer device, memory = %dK\n",
		info->fix.id, pm2fb_fix.smem_len / 1024);

	/*
	 * Our driver data
	 */
	pci_set_drvdata(pdev, info);

	return 0;

 err_exit_all:
	fb_dealloc_cmap(&info->cmap);
 err_exit_both:
	kfree(info->pixmap.addr);
 err_exit_pixmap:
	iounmap(info->screen_base);
	release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
 err_exit_mmio:
	iounmap(default_par->v_regs);
	release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
 err_exit_neither:
	framebuffer_release(info);
	return retval;
}