1/***************************************************************************
2 *  Silicon Motion VoyagerGX framebuffer driver
3 *
4 * 	ported to 2.6 by Embedded Alley Solutions, Inc
5 * 	Copyright (C) 2005 Embedded Alley Solutions, Inc
6 *
7 * 		based on
8    copyright            : (C) 2001 by Szu-Tao Huang
9    email                : johuang@siliconmotion.com
10    Updated to SM501 by Eric.Devolder@amd.com and dan@embeddededge.com
11    for the AMD Mirage Portable Tablet.  20 Oct 2003
12 ***************************************************************************/
13
14/***************************************************************************
15 *                                                                         *
16 *   This program is free software; you can redistribute it and/or modify  *
17 *   it under the terms of the GNU General Public License as published by  *
18 *   the Free Software Foundation; either version 2 of the License, or     *
19 *   (at your option) any later version.                                   *
20 *                                                                         *
21 ***************************************************************************/
22
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/errno.h>
26#include <linux/string.h>
27#include <linux/mm.h>
28#include <linux/tty.h>
29#include <linux/slab.h>
30#include <linux/delay.h>
31#include <linux/fb.h>
32#include <linux/pci.h>
33#include <linux/init.h>
34
35#include <asm/io.h>
36#include <asm/irq.h>
37#include <asm/pgtable.h>
38#include <asm/system.h>
39#include <asm/uaccess.h>
40
41static char __iomem *SMIRegs;	// point to virtual Memory Map IO starting address
42static char __iomem *SMILFB;	// point to virtual video memory starting address
43
44static struct fb_fix_screeninfo smifb_fix __devinitdata = {
45	.id =		"smivgx",
46	.type =		FB_TYPE_PACKED_PIXELS,
47	.visual =	FB_VISUAL_TRUECOLOR,
48	.ywrapstep = 	0,
49	.line_length	= 1024 * 2, /* (bbp * xres)/8 */
50	.accel =	FB_ACCEL_NONE,
51};
52
53static struct fb_var_screeninfo smifb_var __devinitdata = {
54	.xres           = 1024,
55	.yres           = 768,
56	.xres_virtual   = 1024,
57	.yres_virtual   = 768,
58	.bits_per_pixel = 16,
59	.red            = { 11, 5, 0 },
60	.green          = {  5, 6, 0 },
61	.blue           = {  0, 5, 0 },
62	.activate       = FB_ACTIVATE_NOW,
63	.height         = -1,
64	.width          = -1,
65	.vmode          = FB_VMODE_NONINTERLACED,
66};
67
68
69static struct fb_info info;
70
71#define smi_mmiowb(dat,reg)	writeb(dat, (SMIRegs + reg))
72#define smi_mmioww(dat,reg)	writew(dat, (SMIRegs + reg))
73#define smi_mmiowl(dat,reg)	writel(dat, (SMIRegs + reg))
74
75#define smi_mmiorb(reg)	        readb(SMIRegs + reg)
76#define smi_mmiorw(reg)	        readw(SMIRegs + reg)
77#define smi_mmiorl(reg)	        readl(SMIRegs + reg)
78
79/* Address space offsets for various control/status registers.
80*/
81#define MISC_CTRL			0x000004
82#define GPIO_LO_CTRL			0x000008
83#define GPIO_HI_CTRL			0x00000c
84#define DRAM_CTRL			0x000010
85#define CURRENT_POWER_GATE		0x000038
86#define CURRENT_POWER_CLOCK		0x00003C
87#define POWER_MODE1_GATE                0x000048
88#define POWER_MODE1_CLOCK               0x00004C
89#define POWER_MODE_CTRL			0x000054
90
91#define GPIO_DATA_LO			0x010000
92#define GPIO_DATA_HI			0x010004
93#define GPIO_DATA_DIR_LO		0x010008
94#define GPIO_DATA_DIR_HI		0x01000c
95#define I2C_BYTE_COUNT			0x010040
96#define I2C_CONTROL			0x010041
97#define I2C_STATUS_RESET		0x010042
98#define I2C_SLAVE_ADDRESS		0x010043
99#define I2C_DATA			0x010044
100
101#define DE_COLOR_COMPARE		0x100020
102#define DE_COLOR_COMPARE_MASK		0x100024
103#define DE_MASKS			0x100028
104#define DE_WRAP				0x10004C
105
106#define PANEL_DISPLAY_CTRL              0x080000
107#define PANEL_PAN_CTRL                  0x080004
108#define PANEL_COLOR_KEY                 0x080008
109#define PANEL_FB_ADDRESS                0x08000C
110#define PANEL_FB_WIDTH                  0x080010
111#define PANEL_WINDOW_WIDTH              0x080014
112#define PANEL_WINDOW_HEIGHT             0x080018
113#define PANEL_PLANE_TL                  0x08001C
114#define PANEL_PLANE_BR                  0x080020
115#define PANEL_HORIZONTAL_TOTAL          0x080024
116#define PANEL_HORIZONTAL_SYNC           0x080028
117#define PANEL_VERTICAL_TOTAL            0x08002C
118#define PANEL_VERTICAL_SYNC             0x080030
119#define PANEL_CURRENT_LINE              0x080034
120#define VIDEO_DISPLAY_CTRL		0x080040
121#define VIDEO_DISPLAY_FB0		0x080044
122#define VIDEO_DISPLAY_FBWIDTH		0x080048
123#define VIDEO_DISPLAY_FB0LAST		0x08004C
124#define VIDEO_DISPLAY_TL		0x080050
125#define VIDEO_DISPLAY_BR		0x080054
126#define VIDEO_SCALE			0x080058
127#define VIDEO_INITIAL_SCALE		0x08005C
128#define VIDEO_YUV_CONSTANTS		0x080060
129#define VIDEO_DISPLAY_FB1		0x080064
130#define VIDEO_DISPLAY_FB1LAST		0x080068
131#define VIDEO_ALPHA_CTRL		0x080080
132#define PANEL_HWC_ADDRESS		0x0800F0
133#define CRT_DISPLAY_CTRL		0x080200
134#define CRT_FB_ADDRESS			0x080204
135#define CRT_FB_WIDTH			0x080208
136#define CRT_HORIZONTAL_TOTAL		0x08020c
137#define CRT_HORIZONTAL_SYNC		0x080210
138#define CRT_VERTICAL_TOTAL		0x080214
139#define CRT_VERTICAL_SYNC		0x080218
140#define CRT_HWC_ADDRESS			0x080230
141#define CRT_HWC_LOCATION		0x080234
142
143#define ZV_CAPTURE_CTRL			0x090000
144#define ZV_CAPTURE_CLIP			0x090004
145#define ZV_CAPTURE_SIZE			0x090008
146#define ZV_CAPTURE_BUF0			0x09000c
147#define ZV_CAPTURE_BUF1			0x090010
148#define ZV_CAPTURE_OFFSET		0x090014
149#define ZV_FIFO_CTRL			0x090018
150
151#define waitforvsync() udelay(400)
152
153static int initdone = 0;
154static int crt_out = 1;
155
156
157static int
158smi_setcolreg(unsigned regno, unsigned red, unsigned green,
159	unsigned blue, unsigned transp,
160	struct fb_info *info)
161{
162	if (regno > 255)
163		return 1;
164
165	((u32 *)(info->pseudo_palette))[regno] =
166		    ((red & 0xf800) >> 0) |
167		    ((green & 0xfc00) >> 5) |
168		    ((blue & 0xf800) >> 11);
169
170	return 0;
171}
172
173/* This function still needs lots of work to generically support
174 * different output devices (CRT or LCD) and resolutions.
175 * Currently hard-coded for 1024x768 LCD panel.
176 */
177static void smi_setmode(void)
178{
179	if (initdone)
180		return;
181
182	initdone = 1;
183
184	/* Just blast in some control values based upon the chip
185	 * documentation.  We use the internal memory, I don't know
186	 * how to determine the amount available yet.
187	 */
188	smi_mmiowl(0x07F127C2, DRAM_CTRL);
189	smi_mmiowl(0x02000020, PANEL_HWC_ADDRESS);
190	smi_mmiowl(0x007FF800, PANEL_HWC_ADDRESS);
191	smi_mmiowl(0x00021827, POWER_MODE1_GATE);
192	smi_mmiowl(0x011A0A09, POWER_MODE1_CLOCK);
193	smi_mmiowl(0x00000001, POWER_MODE_CTRL);
194	smi_mmiowl(0x80000000, PANEL_FB_ADDRESS);
195	smi_mmiowl(0x08000800, PANEL_FB_WIDTH);
196	smi_mmiowl(0x04000000, PANEL_WINDOW_WIDTH);
197	smi_mmiowl(0x03000000, PANEL_WINDOW_HEIGHT);
198	smi_mmiowl(0x00000000, PANEL_PLANE_TL);
199	smi_mmiowl(0x02FF03FF, PANEL_PLANE_BR);
200	smi_mmiowl(0x05D003FF, PANEL_HORIZONTAL_TOTAL);
201	smi_mmiowl(0x00C80424, PANEL_HORIZONTAL_SYNC);
202	smi_mmiowl(0x032502FF, PANEL_VERTICAL_TOTAL);
203	smi_mmiowl(0x00060302, PANEL_VERTICAL_SYNC);
204	smi_mmiowl(0x00013905, PANEL_DISPLAY_CTRL);
205	smi_mmiowl(0x01013105, PANEL_DISPLAY_CTRL);
206	waitforvsync();
207	smi_mmiowl(0x03013905, PANEL_DISPLAY_CTRL);
208	waitforvsync();
209	smi_mmiowl(0x07013905, PANEL_DISPLAY_CTRL);
210	waitforvsync();
211	smi_mmiowl(0x0F013905, PANEL_DISPLAY_CTRL);
212	smi_mmiowl(0x0002187F, POWER_MODE1_GATE);
213	smi_mmiowl(0x01011801, POWER_MODE1_CLOCK);
214	smi_mmiowl(0x00000001, POWER_MODE_CTRL);
215
216	smi_mmiowl(0x80000000, PANEL_FB_ADDRESS);
217	smi_mmiowl(0x00000000, PANEL_PAN_CTRL);
218	smi_mmiowl(0x00000000, PANEL_COLOR_KEY);
219
220	if (crt_out) {
221		/* Just sent the panel out to the CRT for now.
222		*/
223		smi_mmiowl(0x80000000, CRT_FB_ADDRESS);
224		smi_mmiowl(0x08000800, CRT_FB_WIDTH);
225		smi_mmiowl(0x05D003FF, CRT_HORIZONTAL_TOTAL);
226		smi_mmiowl(0x00C80424, CRT_HORIZONTAL_SYNC);
227		smi_mmiowl(0x032502FF, CRT_VERTICAL_TOTAL);
228		smi_mmiowl(0x00060302, CRT_VERTICAL_SYNC);
229		smi_mmiowl(0x007FF800, CRT_HWC_ADDRESS);
230		smi_mmiowl(0x00010305, CRT_DISPLAY_CTRL);
231		smi_mmiowl(0x00000001, MISC_CTRL);
232	}
233}
234
235/*
236 * Unmap in the memory mapped IO registers
237 *
238 */
239
240static void __devinit smi_unmap_mmio(void)
241{
242	if (SMIRegs) {
243		iounmap(SMIRegs);
244		SMIRegs = NULL;
245	}
246}
247
248
249/*
250 * Unmap in the screen memory
251 *
252 */
253static void __devinit smi_unmap_smem(void)
254{
255	if (SMILFB) {
256		iounmap(SMILFB);
257		SMILFB = NULL;
258	}
259}
260
261static void vgxfb_setup(char *options)
262{
263
264	if (!options || !*options)
265		return;
266
267	/* The only thing I'm looking for right now is to disable a
268	 * CRT output that mirrors the panel display.
269	 */
270	if (strcmp(options, "no_crt") == 0)
271		crt_out = 0;
272
273	return;
274}
275
276static struct fb_ops smifb_ops = {
277	.owner =		THIS_MODULE,
278	.fb_setcolreg =		smi_setcolreg,
279	.fb_fillrect =		cfb_fillrect,
280	.fb_copyarea =		cfb_copyarea,
281	.fb_imageblit =		cfb_imageblit,
282};
283
284static int __devinit vgx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
285{
286	int err;
287
288	/* Enable the chip.
289	*/
290	err = pci_enable_device(dev);
291	if (err)
292		return err;
293
294
295	/* Set up MMIO space.
296	*/
297	smifb_fix.mmio_start = pci_resource_start(dev,1);
298	smifb_fix.mmio_len = 0x00200000;
299	SMIRegs = ioremap(smifb_fix.mmio_start, smifb_fix.mmio_len);
300
301	/* Set up framebuffer.  It's a 64M space, various amount of
302	 * internal memory.  I don't know how to determine the real
303	 * amount of memory (yet).
304	 */
305	smifb_fix.smem_start = pci_resource_start(dev,0);
306	smifb_fix.smem_len = 0x00800000;
307	SMILFB = ioremap(smifb_fix.smem_start, smifb_fix.smem_len);
308
309	memset_io(SMILFB, 0, smifb_fix.smem_len);
310
311	info.screen_base = SMILFB;
312	info.fbops = &smifb_ops;
313	info.fix = smifb_fix;
314
315	info.flags = FBINFO_FLAG_DEFAULT;
316
317	info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
318	if (!info.pseudo_palette) {
319		return -ENOMEM;
320	}
321	memset(info.pseudo_palette, 0, sizeof(u32) *16);
322
323	fb_alloc_cmap(&info.cmap,256,0);
324
325	smi_setmode();
326
327	info.var = smifb_var;
328
329	if (register_framebuffer(&info) < 0)
330		goto failed;
331
332	return 0;
333
334failed:
335	smi_unmap_smem();
336	smi_unmap_mmio();
337
338	return err;
339}
340
341static void __devexit vgx_pci_remove(struct pci_dev *dev)
342{
343	unregister_framebuffer(&info);
344	smi_unmap_smem();
345	smi_unmap_mmio();
346}
347
348static struct pci_device_id vgx_devices[] = {
349	{PCI_VENDOR_ID_SILICON_MOTION, PCI_DEVICE_ID_SM501_VOYAGER_GX_REV_AA,
350	 PCI_ANY_ID, PCI_ANY_ID},
351	{PCI_VENDOR_ID_SILICON_MOTION, PCI_DEVICE_ID_SM501_VOYAGER_GX_REV_B,
352	 PCI_ANY_ID, PCI_ANY_ID},
353	{0}
354};
355
356MODULE_DEVICE_TABLE(pci, vgx_devices);
357
358static struct pci_driver vgxfb_pci_driver = {
359	.name	= "vgxfb",
360	.id_table= vgx_devices,
361	.probe	= vgx_pci_probe,
362	.remove	= __devexit_p(vgx_pci_remove),
363};
364
365static int __init vgxfb_init(void)
366{
367	char *option = NULL;
368
369	if (fb_get_options("vgxfb", &option))
370		return -ENODEV;
371	vgxfb_setup(option);
372
373	printk("Silicon Motion Inc. VOYAGER Init complete.\n");
374	return pci_module_init(&vgxfb_pci_driver);
375}
376
377static void __exit vgxfb_exit(void)
378{
379	pci_unregister_driver(&vgxfb_pci_driver);
380}
381
382module_init(vgxfb_init);
383module_exit(vgxfb_exit);
384
385MODULE_AUTHOR("");
386MODULE_DESCRIPTION("Framebuffer driver for SMI Voyager");
387MODULE_LICENSE("GPL");
388