static int s3_pci_probe()

in fbdev/s3fb.c [1113:1380]


static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
	struct pci_bus_region bus_reg;
	struct resource vga_res;
	struct fb_info *info;
	struct s3fb_info *par;
	int rc;
	u8 regval, cr38, cr39;
	bool found = false;

	/* Ignore secondary VGA device because there is no VGA arbitration */
	if (! svga_primary_device(dev)) {
		dev_info(&(dev->dev), "ignoring secondary device\n");
		return -ENODEV;
	}

	/* Allocate and fill driver data structure */
	info = framebuffer_alloc(sizeof(struct s3fb_info), &(dev->dev));
	if (!info)
		return -ENOMEM;

	par = info->par;
	mutex_init(&par->open_lock);

	info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
	info->fbops = &s3fb_ops;

	/* Prepare PCI device */
	rc = pci_enable_device(dev);
	if (rc < 0) {
		dev_err(info->device, "cannot enable PCI device\n");
		goto err_enable_device;
	}

	rc = pci_request_regions(dev, "s3fb");
	if (rc < 0) {
		dev_err(info->device, "cannot reserve framebuffer region\n");
		goto err_request_regions;
	}


	info->fix.smem_start = pci_resource_start(dev, 0);
	info->fix.smem_len = pci_resource_len(dev, 0);

	/* Map physical IO memory address into kernel space */
	info->screen_base = pci_iomap_wc(dev, 0, 0);
	if (! info->screen_base) {
		rc = -ENOMEM;
		dev_err(info->device, "iomap for framebuffer failed\n");
		goto err_iomap;
	}

	bus_reg.start = 0;
	bus_reg.end = 64 * 1024;

	vga_res.flags = IORESOURCE_IO;

	pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg);

	par->state.vgabase = (void __iomem *) (unsigned long) vga_res.start;

	/* Unlock regs */
	cr38 = vga_rcrt(par->state.vgabase, 0x38);
	cr39 = vga_rcrt(par->state.vgabase, 0x39);
	vga_wseq(par->state.vgabase, 0x08, 0x06);
	vga_wcrt(par->state.vgabase, 0x38, 0x48);
	vga_wcrt(par->state.vgabase, 0x39, 0xA5);

	/* Identify chip type */
	par->chip = id->driver_data & CHIP_MASK;
	par->rev = vga_rcrt(par->state.vgabase, 0x2f);
	if (par->chip & CHIP_UNDECIDED_FLAG)
		par->chip = s3_identification(par);

	/* Find how many physical memory there is on card */
	/* 0x36 register is accessible even if other registers are locked */
	regval = vga_rcrt(par->state.vgabase, 0x36);
	if (par->chip == CHIP_360_TRIO3D_1X ||
	    par->chip == CHIP_362_TRIO3D_2X ||
	    par->chip == CHIP_368_TRIO3D_2X ||
	    par->chip == CHIP_365_TRIO3D) {
		switch ((regval & 0xE0) >> 5) {
		case 0: /* 8MB -- only 4MB usable for display */
		case 1: /* 4MB with 32-bit bus */
		case 2:	/* 4MB */
			info->screen_size = 4 << 20;
			break;
		case 4: /* 2MB on 365 Trio3D */
		case 6: /* 2MB */
			info->screen_size = 2 << 20;
			break;
		}
	} else if (par->chip == CHIP_357_VIRGE_GX2 ||
		   par->chip == CHIP_359_VIRGE_GX2P ||
		   par->chip == CHIP_260_VIRGE_MX) {
		switch ((regval & 0xC0) >> 6) {
		case 1: /* 4MB */
			info->screen_size = 4 << 20;
			break;
		case 3: /* 2MB */
			info->screen_size = 2 << 20;
			break;
		}
	} else if (par->chip == CHIP_988_VIRGE_VX) {
		switch ((regval & 0x60) >> 5) {
		case 0: /* 2MB */
			info->screen_size = 2 << 20;
			break;
		case 1: /* 4MB */
			info->screen_size = 4 << 20;
			break;
		case 2: /* 6MB */
			info->screen_size = 6 << 20;
			break;
		case 3: /* 8MB */
			info->screen_size = 8 << 20;
			break;
		}
		/* off-screen memory */
		regval = vga_rcrt(par->state.vgabase, 0x37);
		switch ((regval & 0x60) >> 5) {
		case 1: /* 4MB */
			info->screen_size -= 4 << 20;
			break;
		case 2: /* 2MB */
			info->screen_size -= 2 << 20;
			break;
		}
	} else
		info->screen_size = s3_memsizes[regval >> 5] << 10;
	info->fix.smem_len = info->screen_size;

	/* Find MCLK frequency */
	regval = vga_rseq(par->state.vgabase, 0x10);
	par->mclk_freq = ((vga_rseq(par->state.vgabase, 0x11) + 2) * 14318) / ((regval & 0x1F)  + 2);
	par->mclk_freq = par->mclk_freq >> (regval >> 5);

	/* Restore locks */
	vga_wcrt(par->state.vgabase, 0x38, cr38);
	vga_wcrt(par->state.vgabase, 0x39, cr39);

	strcpy(info->fix.id, s3_names [par->chip]);
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
	info->fix.type = FB_TYPE_PACKED_PIXELS;
	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
	info->fix.ypanstep = 0;
	info->fix.accel = FB_ACCEL_NONE;
	info->pseudo_palette = (void*) (par->pseudo_palette);
	info->var.bits_per_pixel = 8;

#ifdef CONFIG_FB_S3_DDC
	/* Enable MMIO if needed */
	if (s3fb_ddc_needs_mmio(par->chip)) {
		par->mmio = ioremap(info->fix.smem_start + MMIO_OFFSET, MMIO_SIZE);
		if (par->mmio)
			svga_wcrt_mask(par->state.vgabase, 0x53, 0x08, 0x08);	/* enable MMIO */
		else
			dev_err(info->device, "unable to map MMIO at 0x%lx, disabling DDC",
				info->fix.smem_start + MMIO_OFFSET);
	}
	if (!s3fb_ddc_needs_mmio(par->chip) || par->mmio)
		if (s3fb_setup_ddc_bus(info) == 0) {
			u8 *edid = fb_ddc_read(&par->ddc_adapter);
			par->ddc_registered = true;
			if (edid) {
				fb_edid_to_monspecs(edid, &info->monspecs);
				kfree(edid);
				if (!info->monspecs.modedb)
					dev_err(info->device, "error getting mode database\n");
				else {
					const struct fb_videomode *m;

					fb_videomode_to_modelist(info->monspecs.modedb,
								 info->monspecs.modedb_len,
								 &info->modelist);
					m = fb_find_best_display(&info->monspecs, &info->modelist);
					if (m) {
						fb_videomode_to_var(&info->var, m);
						/* fill all other info->var's fields */
						if (s3fb_check_var(&info->var, info) == 0)
							found = true;
					}
				}
			}
		}
#endif
	if (!mode_option && !found)
		mode_option = "640x480-8@60";

	/* Prepare startup mode */
	if (mode_option) {
		rc = fb_find_mode(&info->var, info, mode_option,
				   info->monspecs.modedb, info->monspecs.modedb_len,
				   NULL, info->var.bits_per_pixel);
		if (!rc || rc == 4) {
			rc = -EINVAL;
			dev_err(info->device, "mode %s not found\n", mode_option);
			fb_destroy_modedb(info->monspecs.modedb);
			info->monspecs.modedb = NULL;
			goto err_find_mode;
		}
	}

	fb_destroy_modedb(info->monspecs.modedb);
	info->monspecs.modedb = NULL;

	/* maximize virtual vertical size for fast scrolling */
	info->var.yres_virtual = info->fix.smem_len * 8 /
			(info->var.bits_per_pixel * info->var.xres_virtual);
	if (info->var.yres_virtual < info->var.yres) {
		dev_err(info->device, "virtual vertical size smaller than real\n");
		rc = -EINVAL;
		goto err_find_mode;
	}

	rc = fb_alloc_cmap(&info->cmap, 256, 0);
	if (rc < 0) {
		dev_err(info->device, "cannot allocate colormap\n");
		goto err_alloc_cmap;
	}

	rc = register_framebuffer(info);
	if (rc < 0) {
		dev_err(info->device, "cannot register framebuffer\n");
		goto err_reg_fb;
	}

	fb_info(info, "%s on %s, %d MB RAM, %d MHz MCLK\n",
		info->fix.id, pci_name(dev),
		info->fix.smem_len >> 20, (par->mclk_freq + 500) / 1000);

	if (par->chip == CHIP_UNKNOWN)
		fb_info(info, "unknown chip, CR2D=%x, CR2E=%x, CRT2F=%x, CRT30=%x\n",
			vga_rcrt(par->state.vgabase, 0x2d),
			vga_rcrt(par->state.vgabase, 0x2e),
			vga_rcrt(par->state.vgabase, 0x2f),
			vga_rcrt(par->state.vgabase, 0x30));

	/* Record a reference to the driver data */
	pci_set_drvdata(dev, info);

	if (mtrr)
		par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
						  info->fix.smem_len);

	return 0;

	/* Error handling */
err_reg_fb:
	fb_dealloc_cmap(&info->cmap);
err_alloc_cmap:
err_find_mode:
#ifdef CONFIG_FB_S3_DDC
	if (par->ddc_registered)
		i2c_del_adapter(&par->ddc_adapter);
	if (par->mmio)
		iounmap(par->mmio);
#endif
	pci_iounmap(dev, info->screen_base);
err_iomap:
	pci_release_regions(dev);
err_request_regions:
/*	pci_disable_device(dev); */
err_enable_device:
	framebuffer_release(info);
	return rc;
}