xref: /openbmc/linux/drivers/video/fbdev/via/via-core.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen  * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved.
4f7018c21STomi Valkeinen  * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.
5f7018c21STomi Valkeinen  * Copyright 2009 Jonathan Corbet <corbet@lwn.net>
6f7018c21STomi Valkeinen  */
7f7018c21STomi Valkeinen 
8f7018c21STomi Valkeinen /*
9f7018c21STomi Valkeinen  * Core code for the Via multifunction framebuffer device.
10f7018c21STomi Valkeinen  */
11145eed48SThomas Zimmermann #include <linux/aperture.h>
12f7018c21STomi Valkeinen #include <linux/via-core.h>
13f7018c21STomi Valkeinen #include <linux/via_i2c.h>
14*d4313a68SLinus Walleij #include "via-gpio.h"
15f7018c21STomi Valkeinen #include "global.h"
16f7018c21STomi Valkeinen 
17f7018c21STomi Valkeinen #include <linux/module.h>
18f7018c21STomi Valkeinen #include <linux/interrupt.h>
19f7018c21STomi Valkeinen #include <linux/platform_device.h>
20f7018c21STomi Valkeinen #include <linux/list.h>
21f7018c21STomi Valkeinen #include <linux/pm.h>
22f7018c21STomi Valkeinen 
23f7018c21STomi Valkeinen /*
24f7018c21STomi Valkeinen  * The default port config.
25f7018c21STomi Valkeinen  */
26f7018c21STomi Valkeinen static struct via_port_cfg adap_configs[] = {
27f7018c21STomi Valkeinen 	[VIA_PORT_26]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x26 },
28f7018c21STomi Valkeinen 	[VIA_PORT_31]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x31 },
29f7018c21STomi Valkeinen 	[VIA_PORT_25]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
30f7018c21STomi Valkeinen 	[VIA_PORT_2C]	= { VIA_PORT_GPIO, VIA_MODE_I2C, VIASR, 0x2c },
31f7018c21STomi Valkeinen 	[VIA_PORT_3D]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d },
32f7018c21STomi Valkeinen 	{ 0, 0, 0, 0 }
33f7018c21STomi Valkeinen };
34f7018c21STomi Valkeinen 
35f7018c21STomi Valkeinen /*
36f7018c21STomi Valkeinen  * The OLPC XO-1.5 puts the camera power and reset lines onto
37f7018c21STomi Valkeinen  * GPIO 2C.
38f7018c21STomi Valkeinen  */
39f7018c21STomi Valkeinen static struct via_port_cfg olpc_adap_configs[] = {
40f7018c21STomi Valkeinen 	[VIA_PORT_26]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x26 },
41f7018c21STomi Valkeinen 	[VIA_PORT_31]	= { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x31 },
42f7018c21STomi Valkeinen 	[VIA_PORT_25]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
43f7018c21STomi Valkeinen 	[VIA_PORT_2C]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c },
44f7018c21STomi Valkeinen 	[VIA_PORT_3D]	= { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d },
45f7018c21STomi Valkeinen 	{ 0, 0, 0, 0 }
46f7018c21STomi Valkeinen };
47f7018c21STomi Valkeinen 
48f7018c21STomi Valkeinen /*
49f7018c21STomi Valkeinen  * We currently only support one viafb device (will there ever be
50f7018c21STomi Valkeinen  * more than one?), so just declare it globally here.
51f7018c21STomi Valkeinen  */
52f7018c21STomi Valkeinen static struct viafb_dev global_dev;
53f7018c21STomi Valkeinen 
54f7018c21STomi Valkeinen 
55f7018c21STomi Valkeinen /*
56f7018c21STomi Valkeinen  * Basic register access; spinlock required.
57f7018c21STomi Valkeinen  */
viafb_mmio_write(int reg,u32 v)58f7018c21STomi Valkeinen static inline void viafb_mmio_write(int reg, u32 v)
59f7018c21STomi Valkeinen {
60f7018c21STomi Valkeinen 	iowrite32(v, global_dev.engine_mmio + reg);
61f7018c21STomi Valkeinen }
62f7018c21STomi Valkeinen 
viafb_mmio_read(int reg)63f7018c21STomi Valkeinen static inline int viafb_mmio_read(int reg)
64f7018c21STomi Valkeinen {
65f7018c21STomi Valkeinen 	return ioread32(global_dev.engine_mmio + reg);
66f7018c21STomi Valkeinen }
67f7018c21STomi Valkeinen 
68f7018c21STomi Valkeinen /* ---------------------------------------------------------------------- */
69f7018c21STomi Valkeinen /*
70f7018c21STomi Valkeinen  * Interrupt management.  We have a single IRQ line for a lot of
71f7018c21STomi Valkeinen  * different functions, so we need to share it.  The design here
72f7018c21STomi Valkeinen  * is that we don't want to reimplement the shared IRQ code here;
73f7018c21STomi Valkeinen  * we also want to avoid having contention for a single handler thread.
74f7018c21STomi Valkeinen  * So each subdev driver which needs interrupts just requests
75f7018c21STomi Valkeinen  * them directly from the kernel.  We just have what's needed for
76f7018c21STomi Valkeinen  * overall access to the interrupt control register.
77f7018c21STomi Valkeinen  */
78f7018c21STomi Valkeinen 
79f7018c21STomi Valkeinen /*
80f7018c21STomi Valkeinen  * Which interrupts are enabled now?
81f7018c21STomi Valkeinen  */
82f7018c21STomi Valkeinen static u32 viafb_enabled_ints;
83f7018c21STomi Valkeinen 
viafb_int_init(void)84f7018c21STomi Valkeinen static void viafb_int_init(void)
85f7018c21STomi Valkeinen {
86f7018c21STomi Valkeinen 	viafb_enabled_ints = 0;
87f7018c21STomi Valkeinen 
88f7018c21STomi Valkeinen 	viafb_mmio_write(VDE_INTERRUPT, 0);
89f7018c21STomi Valkeinen }
90f7018c21STomi Valkeinen 
91f7018c21STomi Valkeinen /*
92f7018c21STomi Valkeinen  * Allow subdevs to ask for specific interrupts to be enabled.  These
93f7018c21STomi Valkeinen  * functions must be called with reg_lock held
94f7018c21STomi Valkeinen  */
viafb_irq_enable(u32 mask)95f7018c21STomi Valkeinen void viafb_irq_enable(u32 mask)
96f7018c21STomi Valkeinen {
97f7018c21STomi Valkeinen 	viafb_enabled_ints |= mask;
98f7018c21STomi Valkeinen 	viafb_mmio_write(VDE_INTERRUPT, viafb_enabled_ints | VDE_I_ENABLE);
99f7018c21STomi Valkeinen }
100f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_irq_enable);
101f7018c21STomi Valkeinen 
viafb_irq_disable(u32 mask)102f7018c21STomi Valkeinen void viafb_irq_disable(u32 mask)
103f7018c21STomi Valkeinen {
104f7018c21STomi Valkeinen 	viafb_enabled_ints &= ~mask;
105f7018c21STomi Valkeinen 	if (viafb_enabled_ints == 0)
106f7018c21STomi Valkeinen 		viafb_mmio_write(VDE_INTERRUPT, 0);  /* Disable entirely */
107f7018c21STomi Valkeinen 	else
108f7018c21STomi Valkeinen 		viafb_mmio_write(VDE_INTERRUPT,
109f7018c21STomi Valkeinen 				viafb_enabled_ints | VDE_I_ENABLE);
110f7018c21STomi Valkeinen }
111f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_irq_disable);
112f7018c21STomi Valkeinen 
113f7018c21STomi Valkeinen /* ---------------------------------------------------------------------- */
114f7018c21STomi Valkeinen /*
115f7018c21STomi Valkeinen  * Currently, the camera driver is the only user of the DMA code, so we
116f7018c21STomi Valkeinen  * only compile it in if the camera driver is being built.  Chances are,
117f7018c21STomi Valkeinen  * most viafb systems will not need to have this extra code for a while.
118f7018c21STomi Valkeinen  * As soon as another user comes long, the ifdef can be removed.
119f7018c21STomi Valkeinen  */
120ab366b40SJavier Martinez Canillas #if IS_ENABLED(CONFIG_VIDEO_VIA_CAMERA)
121f7018c21STomi Valkeinen /*
122f7018c21STomi Valkeinen  * Access to the DMA engine.  This currently provides what the camera
123f7018c21STomi Valkeinen  * driver needs (i.e. outgoing only) but is easily expandable if need
124f7018c21STomi Valkeinen  * be.
125f7018c21STomi Valkeinen  */
126f7018c21STomi Valkeinen 
127f7018c21STomi Valkeinen /*
128f7018c21STomi Valkeinen  * There are four DMA channels in the vx855.  For now, we only
129f7018c21STomi Valkeinen  * use one of them, though.  Most of the time, the DMA channel
130f7018c21STomi Valkeinen  * will be idle, so we keep the IRQ handler unregistered except
131f7018c21STomi Valkeinen  * when some subsystem has indicated an interest.
132f7018c21STomi Valkeinen  */
133f7018c21STomi Valkeinen static int viafb_dma_users;
134f7018c21STomi Valkeinen static DECLARE_COMPLETION(viafb_dma_completion);
135f7018c21STomi Valkeinen /*
136f7018c21STomi Valkeinen  * This mutex protects viafb_dma_users and our global interrupt
137f7018c21STomi Valkeinen  * registration state; it also serializes access to the DMA
138f7018c21STomi Valkeinen  * engine.
139f7018c21STomi Valkeinen  */
140f7018c21STomi Valkeinen static DEFINE_MUTEX(viafb_dma_lock);
141f7018c21STomi Valkeinen 
142f7018c21STomi Valkeinen /*
143f7018c21STomi Valkeinen  * The VX855 DMA descriptor (used for s/g transfers) looks
144f7018c21STomi Valkeinen  * like this.
145f7018c21STomi Valkeinen  */
146f7018c21STomi Valkeinen struct viafb_vx855_dma_descr {
147f7018c21STomi Valkeinen 	u32	addr_low;	/* Low part of phys addr */
148f7018c21STomi Valkeinen 	u32	addr_high;	/* High 12 bits of addr */
149f7018c21STomi Valkeinen 	u32	fb_offset;	/* Offset into FB memory */
150f7018c21STomi Valkeinen 	u32	seg_size;	/* Size, 16-byte units */
151f7018c21STomi Valkeinen 	u32	tile_mode;	/* "tile mode" setting */
152f7018c21STomi Valkeinen 	u32	next_desc_low;	/* Next descriptor addr */
153f7018c21STomi Valkeinen 	u32	next_desc_high;
154f7018c21STomi Valkeinen 	u32	pad;		/* Fill out to 64 bytes */
155f7018c21STomi Valkeinen };
156f7018c21STomi Valkeinen 
157f7018c21STomi Valkeinen /*
158f7018c21STomi Valkeinen  * Flags added to the "next descriptor low" pointers
159f7018c21STomi Valkeinen  */
160f7018c21STomi Valkeinen #define VIAFB_DMA_MAGIC		0x01  /* ??? Just has to be there */
161f7018c21STomi Valkeinen #define VIAFB_DMA_FINAL_SEGMENT 0x02  /* Final segment */
162f7018c21STomi Valkeinen 
163f7018c21STomi Valkeinen /*
164f7018c21STomi Valkeinen  * The completion IRQ handler.
165f7018c21STomi Valkeinen  */
viafb_dma_irq(int irq,void * data)166f7018c21STomi Valkeinen static irqreturn_t viafb_dma_irq(int irq, void *data)
167f7018c21STomi Valkeinen {
168f7018c21STomi Valkeinen 	int csr;
169f7018c21STomi Valkeinen 	irqreturn_t ret = IRQ_NONE;
170f7018c21STomi Valkeinen 
171f7018c21STomi Valkeinen 	spin_lock(&global_dev.reg_lock);
172f7018c21STomi Valkeinen 	csr = viafb_mmio_read(VDMA_CSR0);
173f7018c21STomi Valkeinen 	if (csr & VDMA_C_DONE) {
174f7018c21STomi Valkeinen 		viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE);
175f7018c21STomi Valkeinen 		complete(&viafb_dma_completion);
176f7018c21STomi Valkeinen 		ret = IRQ_HANDLED;
177f7018c21STomi Valkeinen 	}
178f7018c21STomi Valkeinen 	spin_unlock(&global_dev.reg_lock);
179f7018c21STomi Valkeinen 	return ret;
180f7018c21STomi Valkeinen }
181f7018c21STomi Valkeinen 
182f7018c21STomi Valkeinen /*
183f7018c21STomi Valkeinen  * Indicate a need for DMA functionality.
184f7018c21STomi Valkeinen  */
viafb_request_dma(void)185f7018c21STomi Valkeinen int viafb_request_dma(void)
186f7018c21STomi Valkeinen {
187f7018c21STomi Valkeinen 	int ret = 0;
188f7018c21STomi Valkeinen 
189f7018c21STomi Valkeinen 	/*
190f7018c21STomi Valkeinen 	 * Only VX855 is supported currently.
191f7018c21STomi Valkeinen 	 */
192f7018c21STomi Valkeinen 	if (global_dev.chip_type != UNICHROME_VX855)
193f7018c21STomi Valkeinen 		return -ENODEV;
194f7018c21STomi Valkeinen 	/*
195f7018c21STomi Valkeinen 	 * Note the new user and set up our interrupt handler
196f7018c21STomi Valkeinen 	 * if need be.
197f7018c21STomi Valkeinen 	 */
198f7018c21STomi Valkeinen 	mutex_lock(&viafb_dma_lock);
199f7018c21STomi Valkeinen 	viafb_dma_users++;
200f7018c21STomi Valkeinen 	if (viafb_dma_users == 1) {
201f7018c21STomi Valkeinen 		ret = request_irq(global_dev.pdev->irq, viafb_dma_irq,
202f7018c21STomi Valkeinen 				IRQF_SHARED, "via-dma", &viafb_dma_users);
203f7018c21STomi Valkeinen 		if (ret)
204f7018c21STomi Valkeinen 			viafb_dma_users--;
205f7018c21STomi Valkeinen 		else
206f7018c21STomi Valkeinen 			viafb_irq_enable(VDE_I_DMA0TDEN);
207f7018c21STomi Valkeinen 	}
208f7018c21STomi Valkeinen 	mutex_unlock(&viafb_dma_lock);
209f7018c21STomi Valkeinen 	return ret;
210f7018c21STomi Valkeinen }
211f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_request_dma);
212f7018c21STomi Valkeinen 
viafb_release_dma(void)213f7018c21STomi Valkeinen void viafb_release_dma(void)
214f7018c21STomi Valkeinen {
215f7018c21STomi Valkeinen 	mutex_lock(&viafb_dma_lock);
216f7018c21STomi Valkeinen 	viafb_dma_users--;
217f7018c21STomi Valkeinen 	if (viafb_dma_users == 0) {
218f7018c21STomi Valkeinen 		viafb_irq_disable(VDE_I_DMA0TDEN);
219f7018c21STomi Valkeinen 		free_irq(global_dev.pdev->irq, &viafb_dma_users);
220f7018c21STomi Valkeinen 	}
221f7018c21STomi Valkeinen 	mutex_unlock(&viafb_dma_lock);
222f7018c21STomi Valkeinen }
223f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_release_dma);
224f7018c21STomi Valkeinen 
225f7018c21STomi Valkeinen /*
226f7018c21STomi Valkeinen  * Do a scatter/gather DMA copy from FB memory.  You must have done
227f7018c21STomi Valkeinen  * a successful call to viafb_request_dma() first.
228f7018c21STomi Valkeinen  */
viafb_dma_copy_out_sg(unsigned int offset,struct scatterlist * sg,int nsg)229f7018c21STomi Valkeinen int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg)
230f7018c21STomi Valkeinen {
231f7018c21STomi Valkeinen 	struct viafb_vx855_dma_descr *descr;
232f7018c21STomi Valkeinen 	void *descrpages;
233f7018c21STomi Valkeinen 	dma_addr_t descr_handle;
234f7018c21STomi Valkeinen 	unsigned long flags;
235f7018c21STomi Valkeinen 	int i;
236f7018c21STomi Valkeinen 	struct scatterlist *sgentry;
237f7018c21STomi Valkeinen 	dma_addr_t nextdesc;
238f7018c21STomi Valkeinen 
239f7018c21STomi Valkeinen 	/*
240f7018c21STomi Valkeinen 	 * Get a place to put the descriptors.
241f7018c21STomi Valkeinen 	 */
242f7018c21STomi Valkeinen 	descrpages = dma_alloc_coherent(&global_dev.pdev->dev,
243f7018c21STomi Valkeinen 			nsg*sizeof(struct viafb_vx855_dma_descr),
244f7018c21STomi Valkeinen 			&descr_handle, GFP_KERNEL);
245f7018c21STomi Valkeinen 	if (descrpages == NULL) {
246f7018c21STomi Valkeinen 		dev_err(&global_dev.pdev->dev, "Unable to get descr page.\n");
247f7018c21STomi Valkeinen 		return -ENOMEM;
248f7018c21STomi Valkeinen 	}
249f7018c21STomi Valkeinen 	mutex_lock(&viafb_dma_lock);
250f7018c21STomi Valkeinen 	/*
251f7018c21STomi Valkeinen 	 * Fill them in.
252f7018c21STomi Valkeinen 	 */
253f7018c21STomi Valkeinen 	descr = descrpages;
254f7018c21STomi Valkeinen 	nextdesc = descr_handle + sizeof(struct viafb_vx855_dma_descr);
255f7018c21STomi Valkeinen 	for_each_sg(sg, sgentry, nsg, i) {
256f7018c21STomi Valkeinen 		dma_addr_t paddr = sg_dma_address(sgentry);
257f7018c21STomi Valkeinen 		descr->addr_low = paddr & 0xfffffff0;
258f7018c21STomi Valkeinen 		descr->addr_high = ((u64) paddr >> 32) & 0x0fff;
259f7018c21STomi Valkeinen 		descr->fb_offset = offset;
260f7018c21STomi Valkeinen 		descr->seg_size = sg_dma_len(sgentry) >> 4;
261f7018c21STomi Valkeinen 		descr->tile_mode = 0;
262f7018c21STomi Valkeinen 		descr->next_desc_low = (nextdesc&0xfffffff0) | VIAFB_DMA_MAGIC;
263f7018c21STomi Valkeinen 		descr->next_desc_high = ((u64) nextdesc >> 32) & 0x0fff;
264f7018c21STomi Valkeinen 		descr->pad = 0xffffffff;  /* VIA driver does this */
265f7018c21STomi Valkeinen 		offset += sg_dma_len(sgentry);
266f7018c21STomi Valkeinen 		nextdesc += sizeof(struct viafb_vx855_dma_descr);
267f7018c21STomi Valkeinen 		descr++;
268f7018c21STomi Valkeinen 	}
269f7018c21STomi Valkeinen 	descr[-1].next_desc_low = VIAFB_DMA_FINAL_SEGMENT|VIAFB_DMA_MAGIC;
270f7018c21STomi Valkeinen 	/*
271f7018c21STomi Valkeinen 	 * Program the engine.
272f7018c21STomi Valkeinen 	 */
273f7018c21STomi Valkeinen 	spin_lock_irqsave(&global_dev.reg_lock, flags);
274f7018c21STomi Valkeinen 	init_completion(&viafb_dma_completion);
275f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_DQWCR0, 0);
276f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_DONE);
277f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_MR0, VDMA_MR_TDIE | VDMA_MR_CHAIN);
278f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_DPRL0, descr_handle | VIAFB_DMA_MAGIC);
279f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_DPRH0,
280f7018c21STomi Valkeinen 			(((u64)descr_handle >> 32) & 0x0fff) | 0xf0000);
281f7018c21STomi Valkeinen 	(void) viafb_mmio_read(VDMA_CSR0);
282f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_CSR0, VDMA_C_ENABLE|VDMA_C_START);
283f7018c21STomi Valkeinen 	spin_unlock_irqrestore(&global_dev.reg_lock, flags);
284f7018c21STomi Valkeinen 	/*
285f7018c21STomi Valkeinen 	 * Now we just wait until the interrupt handler says
286f7018c21STomi Valkeinen 	 * we're done.  Except that, actually, we need to wait a little
287f7018c21STomi Valkeinen 	 * longer: the interrupts seem to jump the gun a little and we
288f7018c21STomi Valkeinen 	 * get corrupted frames sometimes.
289f7018c21STomi Valkeinen 	 */
290f7018c21STomi Valkeinen 	wait_for_completion_timeout(&viafb_dma_completion, 1);
291f7018c21STomi Valkeinen 	msleep(1);
292f7018c21STomi Valkeinen 	if ((viafb_mmio_read(VDMA_CSR0)&VDMA_C_DONE) == 0)
293f7018c21STomi Valkeinen 		printk(KERN_ERR "VIA DMA timeout!\n");
294f7018c21STomi Valkeinen 	/*
295f7018c21STomi Valkeinen 	 * Clean up and we're done.
296f7018c21STomi Valkeinen 	 */
297f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_CSR0, VDMA_C_DONE);
298f7018c21STomi Valkeinen 	viafb_mmio_write(VDMA_MR0, 0); /* Reset int enable */
299f7018c21STomi Valkeinen 	mutex_unlock(&viafb_dma_lock);
300f7018c21STomi Valkeinen 	dma_free_coherent(&global_dev.pdev->dev,
301f7018c21STomi Valkeinen 			nsg*sizeof(struct viafb_vx855_dma_descr), descrpages,
302f7018c21STomi Valkeinen 			descr_handle);
303f7018c21STomi Valkeinen 	return 0;
304f7018c21STomi Valkeinen }
305f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_dma_copy_out_sg);
306f7018c21STomi Valkeinen #endif /* CONFIG_VIDEO_VIA_CAMERA */
307f7018c21STomi Valkeinen 
308f7018c21STomi Valkeinen /* ---------------------------------------------------------------------- */
309f7018c21STomi Valkeinen /*
310f7018c21STomi Valkeinen  * Figure out how big our framebuffer memory is.  Kind of ugly,
311f7018c21STomi Valkeinen  * but evidently we can't trust the information found in the
312f7018c21STomi Valkeinen  * fbdev configuration area.
313f7018c21STomi Valkeinen  */
314f7018c21STomi Valkeinen static u16 via_function3[] = {
315f7018c21STomi Valkeinen 	CLE266_FUNCTION3, KM400_FUNCTION3, CN400_FUNCTION3, CN700_FUNCTION3,
316f7018c21STomi Valkeinen 	CX700_FUNCTION3, KM800_FUNCTION3, KM890_FUNCTION3, P4M890_FUNCTION3,
317f7018c21STomi Valkeinen 	P4M900_FUNCTION3, VX800_FUNCTION3, VX855_FUNCTION3, VX900_FUNCTION3,
318f7018c21STomi Valkeinen };
319f7018c21STomi Valkeinen 
320f7018c21STomi Valkeinen /* Get the BIOS-configured framebuffer size from PCI configuration space
321f7018c21STomi Valkeinen  * of function 3 in the respective chipset */
viafb_get_fb_size_from_pci(int chip_type)322f7018c21STomi Valkeinen static int viafb_get_fb_size_from_pci(int chip_type)
323f7018c21STomi Valkeinen {
324f7018c21STomi Valkeinen 	int i;
325f7018c21STomi Valkeinen 	u8 offset = 0;
326f7018c21STomi Valkeinen 	u32 FBSize;
327f7018c21STomi Valkeinen 	u32 VideoMemSize;
328f7018c21STomi Valkeinen 
329f7018c21STomi Valkeinen 	/* search for the "FUNCTION3" device in this chipset */
330f7018c21STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(via_function3); i++) {
331f7018c21STomi Valkeinen 		struct pci_dev *pdev;
332f7018c21STomi Valkeinen 
333f7018c21STomi Valkeinen 		pdev = pci_get_device(PCI_VENDOR_ID_VIA, via_function3[i],
334f7018c21STomi Valkeinen 				      NULL);
335f7018c21STomi Valkeinen 		if (!pdev)
336f7018c21STomi Valkeinen 			continue;
337f7018c21STomi Valkeinen 
338f7018c21STomi Valkeinen 		DEBUG_MSG(KERN_INFO "Device ID = %x\n", pdev->device);
339f7018c21STomi Valkeinen 
340f7018c21STomi Valkeinen 		switch (pdev->device) {
341f7018c21STomi Valkeinen 		case CLE266_FUNCTION3:
342f7018c21STomi Valkeinen 		case KM400_FUNCTION3:
343f7018c21STomi Valkeinen 			offset = 0xE0;
344f7018c21STomi Valkeinen 			break;
345f7018c21STomi Valkeinen 		case CN400_FUNCTION3:
346f7018c21STomi Valkeinen 		case CN700_FUNCTION3:
347f7018c21STomi Valkeinen 		case CX700_FUNCTION3:
348f7018c21STomi Valkeinen 		case KM800_FUNCTION3:
349f7018c21STomi Valkeinen 		case KM890_FUNCTION3:
350f7018c21STomi Valkeinen 		case P4M890_FUNCTION3:
351f7018c21STomi Valkeinen 		case P4M900_FUNCTION3:
352f7018c21STomi Valkeinen 		case VX800_FUNCTION3:
353f7018c21STomi Valkeinen 		case VX855_FUNCTION3:
354f7018c21STomi Valkeinen 		case VX900_FUNCTION3:
355f7018c21STomi Valkeinen 		/*case CN750_FUNCTION3: */
356f7018c21STomi Valkeinen 			offset = 0xA0;
357f7018c21STomi Valkeinen 			break;
358f7018c21STomi Valkeinen 		}
359f7018c21STomi Valkeinen 
360f7018c21STomi Valkeinen 		if (!offset)
361f7018c21STomi Valkeinen 			break;
362f7018c21STomi Valkeinen 
363f7018c21STomi Valkeinen 		pci_read_config_dword(pdev, offset, &FBSize);
364f7018c21STomi Valkeinen 		pci_dev_put(pdev);
365f7018c21STomi Valkeinen 	}
366f7018c21STomi Valkeinen 
367f7018c21STomi Valkeinen 	if (!offset) {
368f7018c21STomi Valkeinen 		printk(KERN_ERR "cannot determine framebuffer size\n");
369f7018c21STomi Valkeinen 		return -EIO;
370f7018c21STomi Valkeinen 	}
371f7018c21STomi Valkeinen 
372f7018c21STomi Valkeinen 	FBSize = FBSize & 0x00007000;
373f7018c21STomi Valkeinen 	DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize);
374f7018c21STomi Valkeinen 
375f7018c21STomi Valkeinen 	if (chip_type < UNICHROME_CX700) {
376f7018c21STomi Valkeinen 		switch (FBSize) {
377f7018c21STomi Valkeinen 		case 0x00004000:
378f7018c21STomi Valkeinen 			VideoMemSize = (16 << 20);	/*16M */
379f7018c21STomi Valkeinen 			break;
380f7018c21STomi Valkeinen 
381f7018c21STomi Valkeinen 		case 0x00005000:
382f7018c21STomi Valkeinen 			VideoMemSize = (32 << 20);	/*32M */
383f7018c21STomi Valkeinen 			break;
384f7018c21STomi Valkeinen 
385f7018c21STomi Valkeinen 		case 0x00006000:
386f7018c21STomi Valkeinen 			VideoMemSize = (64 << 20);	/*64M */
387f7018c21STomi Valkeinen 			break;
388f7018c21STomi Valkeinen 
389f7018c21STomi Valkeinen 		default:
390f7018c21STomi Valkeinen 			VideoMemSize = (32 << 20);	/*32M */
391f7018c21STomi Valkeinen 			break;
392f7018c21STomi Valkeinen 		}
393f7018c21STomi Valkeinen 	} else {
394f7018c21STomi Valkeinen 		switch (FBSize) {
395f7018c21STomi Valkeinen 		case 0x00001000:
396f7018c21STomi Valkeinen 			VideoMemSize = (8 << 20);	/*8M */
397f7018c21STomi Valkeinen 			break;
398f7018c21STomi Valkeinen 
399f7018c21STomi Valkeinen 		case 0x00002000:
400f7018c21STomi Valkeinen 			VideoMemSize = (16 << 20);	/*16M */
401f7018c21STomi Valkeinen 			break;
402f7018c21STomi Valkeinen 
403f7018c21STomi Valkeinen 		case 0x00003000:
404f7018c21STomi Valkeinen 			VideoMemSize = (32 << 20);	/*32M */
405f7018c21STomi Valkeinen 			break;
406f7018c21STomi Valkeinen 
407f7018c21STomi Valkeinen 		case 0x00004000:
408f7018c21STomi Valkeinen 			VideoMemSize = (64 << 20);	/*64M */
409f7018c21STomi Valkeinen 			break;
410f7018c21STomi Valkeinen 
411f7018c21STomi Valkeinen 		case 0x00005000:
412f7018c21STomi Valkeinen 			VideoMemSize = (128 << 20);	/*128M */
413f7018c21STomi Valkeinen 			break;
414f7018c21STomi Valkeinen 
415f7018c21STomi Valkeinen 		case 0x00006000:
416f7018c21STomi Valkeinen 			VideoMemSize = (256 << 20);	/*256M */
417f7018c21STomi Valkeinen 			break;
418f7018c21STomi Valkeinen 
419f7018c21STomi Valkeinen 		case 0x00007000:	/* Only on VX855/875 */
420f7018c21STomi Valkeinen 			VideoMemSize = (512 << 20);	/*512M */
421f7018c21STomi Valkeinen 			break;
422f7018c21STomi Valkeinen 
423f7018c21STomi Valkeinen 		default:
424f7018c21STomi Valkeinen 			VideoMemSize = (32 << 20);	/*32M */
425f7018c21STomi Valkeinen 			break;
426f7018c21STomi Valkeinen 		}
427f7018c21STomi Valkeinen 	}
428f7018c21STomi Valkeinen 
429f7018c21STomi Valkeinen 	return VideoMemSize;
430f7018c21STomi Valkeinen }
431f7018c21STomi Valkeinen 
432f7018c21STomi Valkeinen 
433f7018c21STomi Valkeinen /*
434f7018c21STomi Valkeinen  * Figure out and map our MMIO regions.
435f7018c21STomi Valkeinen  */
via_pci_setup_mmio(struct viafb_dev * vdev)436f7018c21STomi Valkeinen static int via_pci_setup_mmio(struct viafb_dev *vdev)
437f7018c21STomi Valkeinen {
438f7018c21STomi Valkeinen 	int ret;
439f7018c21STomi Valkeinen 	/*
440f7018c21STomi Valkeinen 	 * Hook up to the device registers.  Note that we soldier
441f7018c21STomi Valkeinen 	 * on if it fails; the framebuffer can operate (without
442f7018c21STomi Valkeinen 	 * acceleration) without this region.
443f7018c21STomi Valkeinen 	 */
444f7018c21STomi Valkeinen 	vdev->engine_start = pci_resource_start(vdev->pdev, 1);
445f7018c21STomi Valkeinen 	vdev->engine_len = pci_resource_len(vdev->pdev, 1);
4464bdc0d67SChristoph Hellwig 	vdev->engine_mmio = ioremap(vdev->engine_start,
447f7018c21STomi Valkeinen 			vdev->engine_len);
448f7018c21STomi Valkeinen 	if (vdev->engine_mmio == NULL)
449f7018c21STomi Valkeinen 		dev_err(&vdev->pdev->dev,
450f7018c21STomi Valkeinen 				"Unable to map engine MMIO; operation will be "
451f7018c21STomi Valkeinen 				"slow and crippled.\n");
452f7018c21STomi Valkeinen 	/*
453f7018c21STomi Valkeinen 	 * Map in framebuffer memory.  For now, failure here is
454f7018c21STomi Valkeinen 	 * fatal.  Unfortunately, in the absence of significant
455f7018c21STomi Valkeinen 	 * vmalloc space, failure here is also entirely plausible.
456f7018c21STomi Valkeinen 	 * Eventually we want to move away from mapping this
457f7018c21STomi Valkeinen 	 * entire region.
458f7018c21STomi Valkeinen 	 */
459f7018c21STomi Valkeinen 	if (vdev->chip_type == UNICHROME_VX900)
460f7018c21STomi Valkeinen 		vdev->fbmem_start = pci_resource_start(vdev->pdev, 2);
461f7018c21STomi Valkeinen 	else
462f7018c21STomi Valkeinen 		vdev->fbmem_start = pci_resource_start(vdev->pdev, 0);
463f7018c21STomi Valkeinen 	ret = vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type);
464f7018c21STomi Valkeinen 	if (ret < 0)
465f7018c21STomi Valkeinen 		goto out_unmap;
466f7018c21STomi Valkeinen 
467f7018c21STomi Valkeinen 	/* try to map less memory on failure, 8 MB should be still enough */
468f7018c21STomi Valkeinen 	for (; vdev->fbmem_len >= 8 << 20; vdev->fbmem_len /= 2) {
469f7018c21STomi Valkeinen 		vdev->fbmem = ioremap_wc(vdev->fbmem_start, vdev->fbmem_len);
470f7018c21STomi Valkeinen 		if (vdev->fbmem)
471f7018c21STomi Valkeinen 			break;
472f7018c21STomi Valkeinen 	}
473f7018c21STomi Valkeinen 
474f7018c21STomi Valkeinen 	if (vdev->fbmem == NULL) {
475f7018c21STomi Valkeinen 		ret = -ENOMEM;
476f7018c21STomi Valkeinen 		goto out_unmap;
477f7018c21STomi Valkeinen 	}
478f7018c21STomi Valkeinen 	return 0;
479f7018c21STomi Valkeinen out_unmap:
480f7018c21STomi Valkeinen 	iounmap(vdev->engine_mmio);
481f7018c21STomi Valkeinen 	return ret;
482f7018c21STomi Valkeinen }
483f7018c21STomi Valkeinen 
via_pci_teardown_mmio(struct viafb_dev * vdev)484f7018c21STomi Valkeinen static void via_pci_teardown_mmio(struct viafb_dev *vdev)
485f7018c21STomi Valkeinen {
486f7018c21STomi Valkeinen 	iounmap(vdev->fbmem);
487f7018c21STomi Valkeinen 	iounmap(vdev->engine_mmio);
488f7018c21STomi Valkeinen }
489f7018c21STomi Valkeinen 
490f7018c21STomi Valkeinen /*
491f7018c21STomi Valkeinen  * Create our subsidiary devices.
492f7018c21STomi Valkeinen  */
493f7018c21STomi Valkeinen static struct viafb_subdev_info {
494f7018c21STomi Valkeinen 	char *name;
495f7018c21STomi Valkeinen 	struct platform_device *platdev;
496f7018c21STomi Valkeinen } viafb_subdevs[] = {
497f7018c21STomi Valkeinen 	{
498f7018c21STomi Valkeinen 		.name = "viafb-gpio",
499f7018c21STomi Valkeinen 	},
500f7018c21STomi Valkeinen 	{
501f7018c21STomi Valkeinen 		.name = "viafb-i2c",
502f7018c21STomi Valkeinen 	},
503ab366b40SJavier Martinez Canillas #if IS_ENABLED(CONFIG_VIDEO_VIA_CAMERA)
504f7018c21STomi Valkeinen 	{
505f7018c21STomi Valkeinen 		.name = "viafb-camera",
506f7018c21STomi Valkeinen 	},
507f7018c21STomi Valkeinen #endif
508f7018c21STomi Valkeinen };
509f7018c21STomi Valkeinen #define N_SUBDEVS ARRAY_SIZE(viafb_subdevs)
510f7018c21STomi Valkeinen 
via_create_subdev(struct viafb_dev * vdev,struct viafb_subdev_info * info)511f7018c21STomi Valkeinen static int via_create_subdev(struct viafb_dev *vdev,
512f7018c21STomi Valkeinen 			     struct viafb_subdev_info *info)
513f7018c21STomi Valkeinen {
514f7018c21STomi Valkeinen 	int ret;
515f7018c21STomi Valkeinen 
516f7018c21STomi Valkeinen 	info->platdev = platform_device_alloc(info->name, -1);
517f7018c21STomi Valkeinen 	if (!info->platdev) {
518f7018c21STomi Valkeinen 		dev_err(&vdev->pdev->dev, "Unable to allocate pdev %s\n",
519f7018c21STomi Valkeinen 			info->name);
520f7018c21STomi Valkeinen 		return -ENOMEM;
521f7018c21STomi Valkeinen 	}
522f7018c21STomi Valkeinen 	info->platdev->dev.parent = &vdev->pdev->dev;
523f7018c21STomi Valkeinen 	info->platdev->dev.platform_data = vdev;
524f7018c21STomi Valkeinen 	ret = platform_device_add(info->platdev);
525f7018c21STomi Valkeinen 	if (ret) {
526f7018c21STomi Valkeinen 		dev_err(&vdev->pdev->dev, "Unable to add pdev %s\n",
527f7018c21STomi Valkeinen 				info->name);
528f7018c21STomi Valkeinen 		platform_device_put(info->platdev);
529f7018c21STomi Valkeinen 		info->platdev = NULL;
530f7018c21STomi Valkeinen 	}
531f7018c21STomi Valkeinen 	return ret;
532f7018c21STomi Valkeinen }
533f7018c21STomi Valkeinen 
via_setup_subdevs(struct viafb_dev * vdev)534f7018c21STomi Valkeinen static int via_setup_subdevs(struct viafb_dev *vdev)
535f7018c21STomi Valkeinen {
536f7018c21STomi Valkeinen 	int i;
537f7018c21STomi Valkeinen 
538f7018c21STomi Valkeinen 	/*
539f7018c21STomi Valkeinen 	 * Ignore return values.  Even if some of the devices
540f7018c21STomi Valkeinen 	 * fail to be created, we'll still be able to use some
541f7018c21STomi Valkeinen 	 * of the rest.
542f7018c21STomi Valkeinen 	 */
543f7018c21STomi Valkeinen 	for (i = 0; i < N_SUBDEVS; i++)
544f7018c21STomi Valkeinen 		via_create_subdev(vdev, viafb_subdevs + i);
545f7018c21STomi Valkeinen 	return 0;
546f7018c21STomi Valkeinen }
547f7018c21STomi Valkeinen 
via_teardown_subdevs(void)548f7018c21STomi Valkeinen static void via_teardown_subdevs(void)
549f7018c21STomi Valkeinen {
550f7018c21STomi Valkeinen 	int i;
551f7018c21STomi Valkeinen 
552f7018c21STomi Valkeinen 	for (i = 0; i < N_SUBDEVS; i++)
553f7018c21STomi Valkeinen 		if (viafb_subdevs[i].platdev) {
554f7018c21STomi Valkeinen 			viafb_subdevs[i].platdev->dev.platform_data = NULL;
555f7018c21STomi Valkeinen 			platform_device_unregister(viafb_subdevs[i].platdev);
556f7018c21STomi Valkeinen 		}
557f7018c21STomi Valkeinen }
558f7018c21STomi Valkeinen 
559f7018c21STomi Valkeinen /*
560f7018c21STomi Valkeinen  * Power management functions
561f7018c21STomi Valkeinen  */
562ab5fe88aSVaibhav Gupta static __maybe_unused LIST_HEAD(viafb_pm_hooks);
563ab5fe88aSVaibhav Gupta static __maybe_unused DEFINE_MUTEX(viafb_pm_hooks_lock);
564f7018c21STomi Valkeinen 
viafb_pm_register(struct viafb_pm_hooks * hooks)565f7018c21STomi Valkeinen void viafb_pm_register(struct viafb_pm_hooks *hooks)
566f7018c21STomi Valkeinen {
567f7018c21STomi Valkeinen 	INIT_LIST_HEAD(&hooks->list);
568f7018c21STomi Valkeinen 
569f7018c21STomi Valkeinen 	mutex_lock(&viafb_pm_hooks_lock);
570f7018c21STomi Valkeinen 	list_add_tail(&hooks->list, &viafb_pm_hooks);
571f7018c21STomi Valkeinen 	mutex_unlock(&viafb_pm_hooks_lock);
572f7018c21STomi Valkeinen }
573f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_pm_register);
574f7018c21STomi Valkeinen 
viafb_pm_unregister(struct viafb_pm_hooks * hooks)575f7018c21STomi Valkeinen void viafb_pm_unregister(struct viafb_pm_hooks *hooks)
576f7018c21STomi Valkeinen {
577f7018c21STomi Valkeinen 	mutex_lock(&viafb_pm_hooks_lock);
578f7018c21STomi Valkeinen 	list_del(&hooks->list);
579f7018c21STomi Valkeinen 	mutex_unlock(&viafb_pm_hooks_lock);
580f7018c21STomi Valkeinen }
581f7018c21STomi Valkeinen EXPORT_SYMBOL_GPL(viafb_pm_unregister);
582f7018c21STomi Valkeinen 
via_suspend(struct device * dev)583ab5fe88aSVaibhav Gupta static int __maybe_unused via_suspend(struct device *dev)
584f7018c21STomi Valkeinen {
585f7018c21STomi Valkeinen 	struct viafb_pm_hooks *hooks;
586f7018c21STomi Valkeinen 
587f7018c21STomi Valkeinen 	/*
588f7018c21STomi Valkeinen 	 * "I've occasionally hit a few drivers that caused suspend
589f7018c21STomi Valkeinen 	 * failures, and each and every time it was a driver bug, and
590f7018c21STomi Valkeinen 	 * the right thing to do was to just ignore the error and suspend
591f7018c21STomi Valkeinen 	 * anyway - returning an error code and trying to undo the suspend
592f7018c21STomi Valkeinen 	 * is not what anybody ever really wants, even if our model
593f7018c21STomi Valkeinen 	 *_allows_ for it."
594f7018c21STomi Valkeinen 	 * -- Linus Torvalds, Dec. 7, 2009
595f7018c21STomi Valkeinen 	 */
596f7018c21STomi Valkeinen 	mutex_lock(&viafb_pm_hooks_lock);
597f7018c21STomi Valkeinen 	list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list)
598f7018c21STomi Valkeinen 		hooks->suspend(hooks->private);
599f7018c21STomi Valkeinen 	mutex_unlock(&viafb_pm_hooks_lock);
600f7018c21STomi Valkeinen 
601f7018c21STomi Valkeinen 	return 0;
602f7018c21STomi Valkeinen }
603f7018c21STomi Valkeinen 
via_resume(struct device * dev)604ab5fe88aSVaibhav Gupta static int __maybe_unused via_resume(struct device *dev)
605f7018c21STomi Valkeinen {
606f7018c21STomi Valkeinen 	struct viafb_pm_hooks *hooks;
607f7018c21STomi Valkeinen 
608f7018c21STomi Valkeinen 	/* Now bring back any subdevs */
609f7018c21STomi Valkeinen 	mutex_lock(&viafb_pm_hooks_lock);
610f7018c21STomi Valkeinen 	list_for_each_entry(hooks, &viafb_pm_hooks, list)
611f7018c21STomi Valkeinen 		hooks->resume(hooks->private);
612f7018c21STomi Valkeinen 	mutex_unlock(&viafb_pm_hooks_lock);
613f7018c21STomi Valkeinen 
614f7018c21STomi Valkeinen 	return 0;
615f7018c21STomi Valkeinen }
616f7018c21STomi Valkeinen 
via_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)617f7018c21STomi Valkeinen static int via_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
618f7018c21STomi Valkeinen {
619f7018c21STomi Valkeinen 	int ret;
620f7018c21STomi Valkeinen 
621145eed48SThomas Zimmermann 	ret = aperture_remove_conflicting_pci_devices(pdev, "viafb");
622145eed48SThomas Zimmermann 	if (ret)
623145eed48SThomas Zimmermann 		return ret;
624145eed48SThomas Zimmermann 
625f7018c21STomi Valkeinen 	ret = pci_enable_device(pdev);
626f7018c21STomi Valkeinen 	if (ret)
627f7018c21STomi Valkeinen 		return ret;
628f7018c21STomi Valkeinen 
629f7018c21STomi Valkeinen 	/*
630f7018c21STomi Valkeinen 	 * Global device initialization.
631f7018c21STomi Valkeinen 	 */
632f7018c21STomi Valkeinen 	memset(&global_dev, 0, sizeof(global_dev));
633f7018c21STomi Valkeinen 	global_dev.pdev = pdev;
634f7018c21STomi Valkeinen 	global_dev.chip_type = ent->driver_data;
635f7018c21STomi Valkeinen 	global_dev.port_cfg = adap_configs;
636f7018c21STomi Valkeinen 	if (machine_is_olpc())
637f7018c21STomi Valkeinen 		global_dev.port_cfg = olpc_adap_configs;
638f7018c21STomi Valkeinen 
639f7018c21STomi Valkeinen 	spin_lock_init(&global_dev.reg_lock);
640f7018c21STomi Valkeinen 	ret = via_pci_setup_mmio(&global_dev);
641f7018c21STomi Valkeinen 	if (ret)
642f7018c21STomi Valkeinen 		goto out_disable;
643f7018c21STomi Valkeinen 	/*
644f7018c21STomi Valkeinen 	 * Set up interrupts and create our subdevices.  Continue even if
645f7018c21STomi Valkeinen 	 * some things fail.
646f7018c21STomi Valkeinen 	 */
647f7018c21STomi Valkeinen 	viafb_int_init();
648f7018c21STomi Valkeinen 	via_setup_subdevs(&global_dev);
649f7018c21STomi Valkeinen 	/*
650f7018c21STomi Valkeinen 	 * Set up the framebuffer device
651f7018c21STomi Valkeinen 	 */
652f7018c21STomi Valkeinen 	ret = via_fb_pci_probe(&global_dev);
653f7018c21STomi Valkeinen 	if (ret)
654f7018c21STomi Valkeinen 		goto out_subdevs;
655f7018c21STomi Valkeinen 	return 0;
656f7018c21STomi Valkeinen 
657f7018c21STomi Valkeinen out_subdevs:
658f7018c21STomi Valkeinen 	via_teardown_subdevs();
659f7018c21STomi Valkeinen 	via_pci_teardown_mmio(&global_dev);
660f7018c21STomi Valkeinen out_disable:
661f7018c21STomi Valkeinen 	pci_disable_device(pdev);
662f7018c21STomi Valkeinen 	return ret;
663f7018c21STomi Valkeinen }
664f7018c21STomi Valkeinen 
via_pci_remove(struct pci_dev * pdev)665f7018c21STomi Valkeinen static void via_pci_remove(struct pci_dev *pdev)
666f7018c21STomi Valkeinen {
667f7018c21STomi Valkeinen 	via_teardown_subdevs();
668f7018c21STomi Valkeinen 	via_fb_pci_remove(pdev);
669f7018c21STomi Valkeinen 	via_pci_teardown_mmio(&global_dev);
670f7018c21STomi Valkeinen 	pci_disable_device(pdev);
671f7018c21STomi Valkeinen }
672f7018c21STomi Valkeinen 
673f7018c21STomi Valkeinen 
6746e8e55a9SArvind Yadav static const struct pci_device_id via_pci_table[] = {
675f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID),
676f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_CLE266 },
677f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID),
678f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_K400 },
679f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID),
680f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_K800 },
681f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID),
682f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_PM800 },
683f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN700_DID),
684f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_CN700 },
685f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID),
686f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_CX700 },
687f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID),
688f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_CN750 },
689f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID),
690f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_K8M890 },
691f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID),
692f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_P4M890 },
693f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID),
694f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_P4M900 },
695f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID),
696f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_VX800 },
697f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID),
698f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_VX855 },
699f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX900_DID),
700f7018c21STomi Valkeinen 	  .driver_data = UNICHROME_VX900 },
701f7018c21STomi Valkeinen 	{ }
702f7018c21STomi Valkeinen };
703f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, via_pci_table);
704f7018c21STomi Valkeinen 
705ab5fe88aSVaibhav Gupta static const struct dev_pm_ops via_pm_ops = {
706ab5fe88aSVaibhav Gupta #ifdef CONFIG_PM_SLEEP
707ab5fe88aSVaibhav Gupta 	.suspend	= via_suspend,
708ab5fe88aSVaibhav Gupta 	.resume		= via_resume,
709ab5fe88aSVaibhav Gupta 	.freeze		= NULL,
710ab5fe88aSVaibhav Gupta 	.thaw		= via_resume,
711ab5fe88aSVaibhav Gupta 	.poweroff	= NULL,
712ab5fe88aSVaibhav Gupta 	.restore	= via_resume,
713ab5fe88aSVaibhav Gupta #endif
714ab5fe88aSVaibhav Gupta };
715ab5fe88aSVaibhav Gupta 
716f7018c21STomi Valkeinen static struct pci_driver via_driver = {
717f7018c21STomi Valkeinen 	.name		= "viafb",
718f7018c21STomi Valkeinen 	.id_table	= via_pci_table,
719f7018c21STomi Valkeinen 	.probe		= via_pci_probe,
720f7018c21STomi Valkeinen 	.remove		= via_pci_remove,
721ab5fe88aSVaibhav Gupta 	.driver.pm	= &via_pm_ops,
722f7018c21STomi Valkeinen };
723f7018c21STomi Valkeinen 
via_core_init(void)724f7018c21STomi Valkeinen static int __init via_core_init(void)
725f7018c21STomi Valkeinen {
726f7018c21STomi Valkeinen 	int ret;
727f7018c21STomi Valkeinen 
7280ba2fa8cSThomas Zimmermann 	if (fb_modesetting_disabled("viafb"))
7290ba2fa8cSThomas Zimmermann 		return -ENODEV;
7300ba2fa8cSThomas Zimmermann 
731f7018c21STomi Valkeinen 	ret = viafb_init();
732f7018c21STomi Valkeinen 	if (ret)
733f7018c21STomi Valkeinen 		return ret;
734f7018c21STomi Valkeinen 	viafb_i2c_init();
735f7018c21STomi Valkeinen 	viafb_gpio_init();
7365886b130SShang XiaoJing 	ret = pci_register_driver(&via_driver);
7375886b130SShang XiaoJing 	if (ret) {
7385886b130SShang XiaoJing 		viafb_gpio_exit();
7395886b130SShang XiaoJing 		viafb_i2c_exit();
7405886b130SShang XiaoJing 		return ret;
7415886b130SShang XiaoJing 	}
7425886b130SShang XiaoJing 
7435886b130SShang XiaoJing 	return 0;
744f7018c21STomi Valkeinen }
745f7018c21STomi Valkeinen 
via_core_exit(void)746f7018c21STomi Valkeinen static void __exit via_core_exit(void)
747f7018c21STomi Valkeinen {
748f7018c21STomi Valkeinen 	pci_unregister_driver(&via_driver);
749f7018c21STomi Valkeinen 	viafb_gpio_exit();
750f7018c21STomi Valkeinen 	viafb_i2c_exit();
751f7018c21STomi Valkeinen 	viafb_exit();
752f7018c21STomi Valkeinen }
753f7018c21STomi Valkeinen 
754f7018c21STomi Valkeinen module_init(via_core_init);
755f7018c21STomi Valkeinen module_exit(via_core_exit);
756