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