diff options
Diffstat (limited to 'drivers/video/fbdev/via/via-core.c')
| -rw-r--r-- | drivers/video/fbdev/via/via-core.c | 790 | 
1 files changed, 790 insertions, 0 deletions
diff --git a/drivers/video/fbdev/via/via-core.c b/drivers/video/fbdev/via/via-core.c new file mode 100644 index 00000000000..6e274825fb3 --- /dev/null +++ b/drivers/video/fbdev/via/via-core.c @@ -0,0 +1,790 @@ +/* + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2009 Jonathan Corbet <corbet@lwn.net> + */ + +/* + * Core code for the Via multifunction framebuffer device. + */ +#include <linux/via-core.h> +#include <linux/via_i2c.h> +#include <linux/via-gpio.h> +#include "global.h" + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/pm.h> +#include <asm/olpc.h> + +/* + * The default port config. + */ +static struct via_port_cfg adap_configs[] = { +	[VIA_PORT_26]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x26 }, +	[VIA_PORT_31]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x31 }, +	[VIA_PORT_25]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, +	[VIA_PORT_2C]	= { VIA_PORT_GPIO, VIA_MODE_I2C, VIASR, 0x2c }, +	[VIA_PORT_3D]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, +	{ 0, 0, 0, 0 } +}; + +/* + * The OLPC XO-1.5 puts the camera power and reset lines onto + * GPIO 2C. + */ +static struct via_port_cfg olpc_adap_configs[] = { +	[VIA_PORT_26]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x26 }, +	[VIA_PORT_31]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x31 }, +	[VIA_PORT_25]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, +	[VIA_PORT_2C]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c }, +	[VIA_PORT_3D]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, +	{ 0, 0, 0, 0 } +}; + +/* + * We currently only support one viafb device (will there ever be + * more than one?), so just declare it globally here. + */ +static struct viafb_dev global_dev; + + +/* + * Basic register access; spinlock required. + */ +static inline void viafb_mmio_write(int reg, u32 v) +{ +	iowrite32(v, global_dev.engine_mmio + reg); +} + +static inline int viafb_mmio_read(int reg) +{ +	return ioread32(global_dev.engine_mmio + reg); +} + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt management.  We have a single IRQ line for a lot of + * different functions, so we need to share it.  The design here + * is that we don't want to reimplement the shared IRQ code here; + * we also want to avoid having contention for a single handler thread. + * So each subdev driver which needs interrupts just requests + * them directly from the kernel.  We just have what's needed for + * overall access to the interrupt control register. + */ + +/* + * Which interrupts are enabled now? + */ +static u32 viafb_enabled_ints; + +static void viafb_int_init(void) +{ +	viafb_enabled_ints = 0; + +	viafb_mmio_write(VDE_INTERRUPT, 0); +} + +/* + * Allow subdevs to ask for specific interrupts to be enabled.  These + * functions must be called with reg_lock held + */ +void viafb_irq_enable(u32 mask) +{ +	viafb_enabled_ints |= mask; +	viafb_mmio_write(VDE_INTERRUPT, viafb_enabled_ints | VDE_I_ENABLE); +} +EXPORT_SYMBOL_GPL(viafb_irq_enable); + +void viafb_irq_disable(u32 mask) +{ +	viafb_enabled_ints &= ~mask; +	if (viafb_enabled_ints == 0) +		viafb_mmio_write(VDE_INTERRUPT, 0);  /* Disable entirely */ +	else +		viafb_mmio_write(VDE_INTERRUPT, +				viafb_enabled_ints | VDE_I_ENABLE); +} +EXPORT_SYMBOL_GPL(viafb_irq_disable); + +/* ---------------------------------------------------------------------- */ +/* + * Currently, the camera driver is the only user of the DMA code, so we + * only compile it in if the camera driver is being built.  Chances are, + * most viafb systems will not need to have this extra code for a while. + * As soon as another user comes long, the ifdef can be removed. + */ +#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE) +/* + * Access to the DMA engine.  This currently provides what the camera + * driver needs (i.e. outgoing only) but is easily expandable if need + * be. + */ + +/* + * There are four DMA channels in the vx855.  For now, we only + * use one of them, though.  Most of the time, the DMA channel + * will be idle, so we keep the IRQ handler unregistered except + * when some subsystem has indicated an interest. + */ +static int viafb_dma_users; +static DECLARE_COMPLETION(viafb_dma_completion); +/* + * This mutex protects viafb_dma_users and our global interrupt + * registration state; it also serializes access to the DMA + * engine. + */ +static DEFINE_MUTEX(viafb_dma_lock); + +/* + * The VX855 DMA descriptor (used for s/g transfers) looks + * like this. + */ +struct viafb_vx855_dma_descr { +	u32	addr_low;	/* Low part of phys addr */ +	u32	addr_high;	/* High 12 bits of addr */ +	u32	fb_offset;	/* Offset into FB memory */ +	u32	seg_size;	/* Size, 16-byte units */ +	u32	tile_mode;	/* "tile mode" setting */ +	u32	next_desc_low;	/* Next descriptor addr */ +	u32	next_desc_high; +	u32	pad;		/* Fill out to 64 bytes */ +}; + +/* + * Flags added to the "next descriptor low" pointers + */ +#define VIAFB_DMA_MAGIC		0x01  /* ??? Just has to be there */ +#define VIAFB_DMA_FINAL_SEGMENT 0x02  /* Final segment */ + +/* + * The completion IRQ handler. + */ +static irqreturn_t viafb_dma_irq(int irq, void *data) +{ +	int csr; +	irqreturn_t ret = IRQ_NONE; + +	spin_lock(&global_dev.reg_lock); +	csr = viafb_mmio_read(VDMA_CSR0); +	if (csr & VDMA_C_DONE) { +		viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE); +		complete(&viafb_dma_completion); +		ret = IRQ_HANDLED; +	} +	spin_unlock(&global_dev.reg_lock); +	return ret; +} + +/* + * Indicate a need for DMA functionality. + */ +int viafb_request_dma(void) +{ +	int ret = 0; + +	/* +	 * Only VX855 is supported currently. +	 */ +	if (global_dev.chip_type != UNICHROME_VX855) +		return -ENODEV; +	/* +	 * Note the new user and set up our interrupt handler +	 * if need be. +	 */ +	mutex_lock(&viafb_dma_lock); +	viafb_dma_users++; +	if (viafb_dma_users == 1) { +		ret = request_irq(global_dev.pdev->irq, viafb_dma_irq, +				IRQF_SHARED, "via-dma", &viafb_dma_users); +		if (ret) +			viafb_dma_users--; +		else +			viafb_irq_enable(VDE_I_DMA0TDEN); +	} +	mutex_unlock(&viafb_dma_lock); +	return ret; +} +EXPORT_SYMBOL_GPL(viafb_request_dma); + +void viafb_release_dma(void) +{ +	mutex_lock(&viafb_dma_lock); +	viafb_dma_users--; +	if (viafb_dma_users == 0) { +		viafb_irq_disable(VDE_I_DMA0TDEN); +		free_irq(global_dev.pdev->irq, &viafb_dma_users); +	} +	mutex_unlock(&viafb_dma_lock); +} +EXPORT_SYMBOL_GPL(viafb_release_dma); + + +#if 0 +/* + * Copy a single buffer from FB memory, synchronously.  This code works + * but is not currently used. + */ +void viafb_dma_copy_out(unsigned int offset, dma_addr_t paddr, int len) +{ +	unsigned long flags; +	int csr; + +	mutex_lock(&viafb_dma_lock); +	init_completion(&viafb_dma_completion); +	/* +	 * Program the controller. +	 */ +	spin_lock_irqsave(&global_dev.reg_lock, flags); +	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE); +	/* Enable ints; must happen after CSR0 write! */ +	viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE); +	viafb_mmio_write(VDMA_MARL0, (int) (paddr & 0xfffffff0)); +	viafb_mmio_write(VDMA_MARH0, (int) ((paddr >> 28) & 0xfff)); +	/* Data sheet suggests DAR0 should be <<4, but it lies */ +	viafb_mmio_write(VDMA_DAR0, offset); +	viafb_mmio_write(VDMA_DQWCR0, len >> 4); +	viafb_mmio_write(VDMA_TMR0, 0); +	viafb_mmio_write(VDMA_DPRL0, 0); +	viafb_mmio_write(VDMA_DPRH0, 0); +	viafb_mmio_write(VDMA_PMR0, 0); +	csr = viafb_mmio_read(VDMA_CSR0); +	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START); +	spin_unlock_irqrestore(&global_dev.reg_lock, flags); +	/* +	 * Now we just wait until the interrupt handler says +	 * we're done. +	 */ +	wait_for_completion_interruptible(&viafb_dma_completion); +	viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */ +	mutex_unlock(&viafb_dma_lock); +} +EXPORT_SYMBOL_GPL(viafb_dma_copy_out); +#endif + +/* + * Do a scatter/gather DMA copy from FB memory.  You must have done + * a successful call to viafb_request_dma() first. + */ +int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg) +{ +	struct viafb_vx855_dma_descr *descr; +	void *descrpages; +	dma_addr_t descr_handle; +	unsigned long flags; +	int i; +	struct scatterlist *sgentry; +	dma_addr_t nextdesc; + +	/* +	 * Get a place to put the descriptors. +	 */ +	descrpages = dma_alloc_coherent(&global_dev.pdev->dev, +			nsg*sizeof(struct viafb_vx855_dma_descr), +			&descr_handle, GFP_KERNEL); +	if (descrpages == NULL) { +		dev_err(&global_dev.pdev->dev, "Unable to get descr page.\n"); +		return -ENOMEM; +	} +	mutex_lock(&viafb_dma_lock); +	/* +	 * Fill them in. +	 */ +	descr = descrpages; +	nextdesc = descr_handle + sizeof(struct viafb_vx855_dma_descr); +	for_each_sg(sg, sgentry, nsg, i) { +		dma_addr_t paddr = sg_dma_address(sgentry); +		descr->addr_low = paddr & 0xfffffff0; +		descr->addr_high = ((u64) paddr >> 32) & 0x0fff; +		descr->fb_offset = offset; +		descr->seg_size = sg_dma_len(sgentry) >> 4; +		descr->tile_mode = 0; +		descr->next_desc_low = (nextdesc&0xfffffff0) | VIAFB_DMA_MAGIC; +		descr->next_desc_high = ((u64) nextdesc >> 32) & 0x0fff; +		descr->pad = 0xffffffff;  /* VIA driver does this */ +		offset += sg_dma_len(sgentry); +		nextdesc += sizeof(struct viafb_vx855_dma_descr); +		descr++; +	} +	descr[-1].next_desc_low = VIAFB_DMA_FINAL_SEGMENT|VIAFB_DMA_MAGIC; +	/* +	 * Program the engine. +	 */ +	spin_lock_irqsave(&global_dev.reg_lock, flags); +	init_completion(&viafb_dma_completion); +	viafb_mmio_write(VDMA_DQWCR0, 0); +	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE); +	viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE | VDMA_MR_CHAIN); +	viafb_mmio_write(VDMA_DPRL0, descr_handle | VIAFB_DMA_MAGIC); +	viafb_mmio_write(VDMA_DPRH0, +			(((u64)descr_handle >> 32) & 0x0fff) | 0xf0000); +	(void) viafb_mmio_read(VDMA_CSR0); +	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START); +	spin_unlock_irqrestore(&global_dev.reg_lock, flags); +	/* +	 * Now we just wait until the interrupt handler says +	 * we're done.  Except that, actually, we need to wait a little +	 * longer: the interrupts seem to jump the gun a little and we +	 * get corrupted frames sometimes. +	 */ +	wait_for_completion_timeout(&viafb_dma_completion, 1); +	msleep(1); +	if ((viafb_mmio_read(VDMA_CSR0)&VDMA_C_DONE) == 0) +		printk(KERN_ERR "VIA DMA timeout!\n"); +	/* +	 * Clean up and we're done. +	 */ +	viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE); +	viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */ +	mutex_unlock(&viafb_dma_lock); +	dma_free_coherent(&global_dev.pdev->dev, +			nsg*sizeof(struct viafb_vx855_dma_descr), descrpages, +			descr_handle); +	return 0; +} +EXPORT_SYMBOL_GPL(viafb_dma_copy_out_sg); +#endif /* CONFIG_VIDEO_VIA_CAMERA */ + +/* ---------------------------------------------------------------------- */ +/* + * Figure out how big our framebuffer memory is.  Kind of ugly, + * but evidently we can't trust the information found in the + * fbdev configuration area. + */ +static u16 via_function3[] = { +	CLE266_FUNCTION3, KM400_FUNCTION3, CN400_FUNCTION3, CN700_FUNCTION3, +	CX700_FUNCTION3, KM800_FUNCTION3, KM890_FUNCTION3, P4M890_FUNCTION3, +	P4M900_FUNCTION3, VX800_FUNCTION3, VX855_FUNCTION3, VX900_FUNCTION3, +}; + +/* Get the BIOS-configured framebuffer size from PCI configuration space + * of function 3 in the respective chipset */ +static int viafb_get_fb_size_from_pci(int chip_type) +{ +	int i; +	u8 offset = 0; +	u32 FBSize; +	u32 VideoMemSize; + +	/* search for the "FUNCTION3" device in this chipset */ +	for (i = 0; i < ARRAY_SIZE(via_function3); i++) { +		struct pci_dev *pdev; + +		pdev = pci_get_device(PCI_VENDOR_ID_VIA, via_function3[i], +				      NULL); +		if (!pdev) +			continue; + +		DEBUG_MSG(KERN_INFO "Device ID = %x\n", pdev->device); + +		switch (pdev->device) { +		case CLE266_FUNCTION3: +		case KM400_FUNCTION3: +			offset = 0xE0; +			break; +		case CN400_FUNCTION3: +		case CN700_FUNCTION3: +		case CX700_FUNCTION3: +		case KM800_FUNCTION3: +		case KM890_FUNCTION3: +		case P4M890_FUNCTION3: +		case P4M900_FUNCTION3: +		case VX800_FUNCTION3: +		case VX855_FUNCTION3: +		case VX900_FUNCTION3: +		/*case CN750_FUNCTION3: */ +			offset = 0xA0; +			break; +		} + +		if (!offset) +			break; + +		pci_read_config_dword(pdev, offset, &FBSize); +		pci_dev_put(pdev); +	} + +	if (!offset) { +		printk(KERN_ERR "cannot determine framebuffer size\n"); +		return -EIO; +	} + +	FBSize = FBSize & 0x00007000; +	DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize); + +	if (chip_type < UNICHROME_CX700) { +		switch (FBSize) { +		case 0x00004000: +			VideoMemSize = (16 << 20);	/*16M */ +			break; + +		case 0x00005000: +			VideoMemSize = (32 << 20);	/*32M */ +			break; + +		case 0x00006000: +			VideoMemSize = (64 << 20);	/*64M */ +			break; + +		default: +			VideoMemSize = (32 << 20);	/*32M */ +			break; +		} +	} else { +		switch (FBSize) { +		case 0x00001000: +			VideoMemSize = (8 << 20);	/*8M */ +			break; + +		case 0x00002000: +			VideoMemSize = (16 << 20);	/*16M */ +			break; + +		case 0x00003000: +			VideoMemSize = (32 << 20);	/*32M */ +			break; + +		case 0x00004000: +			VideoMemSize = (64 << 20);	/*64M */ +			break; + +		case 0x00005000: +			VideoMemSize = (128 << 20);	/*128M */ +			break; + +		case 0x00006000: +			VideoMemSize = (256 << 20);	/*256M */ +			break; + +		case 0x00007000:	/* Only on VX855/875 */ +			VideoMemSize = (512 << 20);	/*512M */ +			break; + +		default: +			VideoMemSize = (32 << 20);	/*32M */ +			break; +		} +	} + +	return VideoMemSize; +} + + +/* + * Figure out and map our MMIO regions. + */ +static int via_pci_setup_mmio(struct viafb_dev *vdev) +{ +	int ret; +	/* +	 * Hook up to the device registers.  Note that we soldier +	 * on if it fails; the framebuffer can operate (without +	 * acceleration) without this region. +	 */ +	vdev->engine_start = pci_resource_start(vdev->pdev, 1); +	vdev->engine_len = pci_resource_len(vdev->pdev, 1); +	vdev->engine_mmio = ioremap_nocache(vdev->engine_start, +			vdev->engine_len); +	if (vdev->engine_mmio == NULL) +		dev_err(&vdev->pdev->dev, +				"Unable to map engine MMIO; operation will be " +				"slow and crippled.\n"); +	/* +	 * Map in framebuffer memory.  For now, failure here is +	 * fatal.  Unfortunately, in the absence of significant +	 * vmalloc space, failure here is also entirely plausible. +	 * Eventually we want to move away from mapping this +	 * entire region. +	 */ +	if (vdev->chip_type == UNICHROME_VX900) +		vdev->fbmem_start = pci_resource_start(vdev->pdev, 2); +	else +		vdev->fbmem_start = pci_resource_start(vdev->pdev, 0); +	ret = vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type); +	if (ret < 0) +		goto out_unmap; + +	/* try to map less memory on failure, 8 MB should be still enough */ +	for (; vdev->fbmem_len >= 8 << 20; vdev->fbmem_len /= 2) { +		vdev->fbmem = ioremap_wc(vdev->fbmem_start, vdev->fbmem_len); +		if (vdev->fbmem) +			break; +	} + +	if (vdev->fbmem == NULL) { +		ret = -ENOMEM; +		goto out_unmap; +	} +	return 0; +out_unmap: +	iounmap(vdev->engine_mmio); +	return ret; +} + +static void via_pci_teardown_mmio(struct viafb_dev *vdev) +{ +	iounmap(vdev->fbmem); +	iounmap(vdev->engine_mmio); +} + +/* + * Create our subsidiary devices. + */ +static struct viafb_subdev_info { +	char *name; +	struct platform_device *platdev; +} viafb_subdevs[] = { +	{ +		.name = "viafb-gpio", +	}, +	{ +		.name = "viafb-i2c", +	}, +#if defined(CONFIG_VIDEO_VIA_CAMERA) || defined(CONFIG_VIDEO_VIA_CAMERA_MODULE) +	{ +		.name = "viafb-camera", +	}, +#endif +}; +#define N_SUBDEVS ARRAY_SIZE(viafb_subdevs) + +static int via_create_subdev(struct viafb_dev *vdev, +			     struct viafb_subdev_info *info) +{ +	int ret; + +	info->platdev = platform_device_alloc(info->name, -1); +	if (!info->platdev) { +		dev_err(&vdev->pdev->dev, "Unable to allocate pdev %s\n", +			info->name); +		return -ENOMEM; +	} +	info->platdev->dev.parent = &vdev->pdev->dev; +	info->platdev->dev.platform_data = vdev; +	ret = platform_device_add(info->platdev); +	if (ret) { +		dev_err(&vdev->pdev->dev, "Unable to add pdev %s\n", +				info->name); +		platform_device_put(info->platdev); +		info->platdev = NULL; +	} +	return ret; +} + +static int via_setup_subdevs(struct viafb_dev *vdev) +{ +	int i; + +	/* +	 * Ignore return values.  Even if some of the devices +	 * fail to be created, we'll still be able to use some +	 * of the rest. +	 */ +	for (i = 0; i < N_SUBDEVS; i++) +		via_create_subdev(vdev, viafb_subdevs + i); +	return 0; +} + +static void via_teardown_subdevs(void) +{ +	int i; + +	for (i = 0; i < N_SUBDEVS; i++) +		if (viafb_subdevs[i].platdev) { +			viafb_subdevs[i].platdev->dev.platform_data = NULL; +			platform_device_unregister(viafb_subdevs[i].platdev); +		} +} + +/* + * Power management functions + */ +#ifdef CONFIG_PM +static LIST_HEAD(viafb_pm_hooks); +static DEFINE_MUTEX(viafb_pm_hooks_lock); + +void viafb_pm_register(struct viafb_pm_hooks *hooks) +{ +	INIT_LIST_HEAD(&hooks->list); + +	mutex_lock(&viafb_pm_hooks_lock); +	list_add_tail(&hooks->list, &viafb_pm_hooks); +	mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_register); + +void viafb_pm_unregister(struct viafb_pm_hooks *hooks) +{ +	mutex_lock(&viafb_pm_hooks_lock); +	list_del(&hooks->list); +	mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_unregister); + +static int via_suspend(struct pci_dev *pdev, pm_message_t state) +{ +	struct viafb_pm_hooks *hooks; + +	if (state.event != PM_EVENT_SUSPEND) +		return 0; +	/* +	 * "I've occasionally hit a few drivers that caused suspend +	 * failures, and each and every time it was a driver bug, and +	 * the right thing to do was to just ignore the error and suspend +	 * anyway - returning an error code and trying to undo the suspend +	 * is not what anybody ever really wants, even if our model +	 *_allows_ for it." +	 * -- Linus Torvalds, Dec. 7, 2009 +	 */ +	mutex_lock(&viafb_pm_hooks_lock); +	list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list) +		hooks->suspend(hooks->private); +	mutex_unlock(&viafb_pm_hooks_lock); + +	pci_save_state(pdev); +	pci_disable_device(pdev); +	pci_set_power_state(pdev, pci_choose_state(pdev, state)); +	return 0; +} + +static int via_resume(struct pci_dev *pdev) +{ +	struct viafb_pm_hooks *hooks; + +	/* Get the bus side powered up */ +	pci_set_power_state(pdev, PCI_D0); +	pci_restore_state(pdev); +	if (pci_enable_device(pdev)) +		return 0; + +	pci_set_master(pdev); + +	/* Now bring back any subdevs */ +	mutex_lock(&viafb_pm_hooks_lock); +	list_for_each_entry(hooks, &viafb_pm_hooks, list) +		hooks->resume(hooks->private); +	mutex_unlock(&viafb_pm_hooks_lock); + +	return 0; +} +#endif /* CONFIG_PM */ + +static int via_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ +	int ret; + +	ret = pci_enable_device(pdev); +	if (ret) +		return ret; + +	/* +	 * Global device initialization. +	 */ +	memset(&global_dev, 0, sizeof(global_dev)); +	global_dev.pdev = pdev; +	global_dev.chip_type = ent->driver_data; +	global_dev.port_cfg = adap_configs; +	if (machine_is_olpc()) +		global_dev.port_cfg = olpc_adap_configs; + +	spin_lock_init(&global_dev.reg_lock); +	ret = via_pci_setup_mmio(&global_dev); +	if (ret) +		goto out_disable; +	/* +	 * Set up interrupts and create our subdevices.  Continue even if +	 * some things fail. +	 */ +	viafb_int_init(); +	via_setup_subdevs(&global_dev); +	/* +	 * Set up the framebuffer device +	 */ +	ret = via_fb_pci_probe(&global_dev); +	if (ret) +		goto out_subdevs; +	return 0; + +out_subdevs: +	via_teardown_subdevs(); +	via_pci_teardown_mmio(&global_dev); +out_disable: +	pci_disable_device(pdev); +	return ret; +} + +static void via_pci_remove(struct pci_dev *pdev) +{ +	via_teardown_subdevs(); +	via_fb_pci_remove(pdev); +	via_pci_teardown_mmio(&global_dev); +	pci_disable_device(pdev); +} + + +static struct pci_device_id via_pci_table[] = { +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), +	  .driver_data = UNICHROME_CLE266 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), +	  .driver_data = UNICHROME_K400 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), +	  .driver_data = UNICHROME_K800 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), +	  .driver_data = UNICHROME_PM800 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN700_DID), +	  .driver_data = UNICHROME_CN700 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), +	  .driver_data = UNICHROME_CX700 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), +	  .driver_data = UNICHROME_CN750 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), +	  .driver_data = UNICHROME_K8M890 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), +	  .driver_data = UNICHROME_P4M890 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), +	  .driver_data = UNICHROME_P4M900 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), +	  .driver_data = UNICHROME_VX800 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), +	  .driver_data = UNICHROME_VX855 }, +	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX900_DID), +	  .driver_data = UNICHROME_VX900 }, +	{ } +}; +MODULE_DEVICE_TABLE(pci, via_pci_table); + +static struct pci_driver via_driver = { +	.name		= "viafb", +	.id_table	= via_pci_table, +	.probe		= via_pci_probe, +	.remove		= via_pci_remove, +#ifdef CONFIG_PM +	.suspend	= via_suspend, +	.resume		= via_resume, +#endif +}; + +static int __init via_core_init(void) +{ +	int ret; + +	ret = viafb_init(); +	if (ret) +		return ret; +	viafb_i2c_init(); +	viafb_gpio_init(); +	return pci_register_driver(&via_driver); +} + +static void __exit via_core_exit(void) +{ +	pci_unregister_driver(&via_driver); +	viafb_gpio_exit(); +	viafb_i2c_exit(); +	viafb_exit(); +} + +module_init(via_core_init); +module_exit(via_core_exit);  | 
