1/*
2 * linux/drivers/video/w100fb.c
3 *
4 * Frame Buffer Device for ATI Imageon w100 (Wallaby)
5 *
6 * Copyright (C) 2002, ATI Corp.
7 * Copyright (C) 2004-2006 Richard Purdie
8 * Copyright (c) 2005 Ian Molton
9 * Copyright (c) 2006 Alberto Mardegan
10 *
11 * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
12 *
13 * Generic platform support by Ian Molton <spyro@f2s.com>
14 * and Richard Purdie <rpurdie@rpsys.net>
15 *
16 * w32xx support by Ian Molton
17 *
18 * Hardware acceleration support by Alberto Mardegan
19 * <mardy@users.sourceforge.net>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License version 2 as
23 * published by the Free Software Foundation.
24 *
25 */
26
27#include <linux/delay.h>
28#include <linux/fb.h>
29#include <linux/init.h>
30#include <linux/kernel.h>
31#include <linux/mm.h>
32#include <linux/platform_device.h>
33#include <linux/slab.h>
34#include <linux/string.h>
35#include <linux/vmalloc.h>
36#include <linux/module.h>
37#include <asm/io.h>
38#include <asm/uaccess.h>
39#include <video/w100fb.h>
40#include "w100fb.h"
41
42/*
43 * Prototypes
44 */
45static void w100_suspend(u32 mode);
46static void w100_vsync(void);
47static void w100_hw_init(struct w100fb_par*);
48static void w100_pwm_setup(struct w100fb_par*);
49static void w100_init_clocks(struct w100fb_par*);
50static void w100_setup_memory(struct w100fb_par*);
51static void w100_init_lcd(struct w100fb_par*);
52static void w100_set_dispregs(struct w100fb_par*);
53static void w100_update_enable(void);
54static void w100_update_disable(void);
55static void calc_hsync(struct w100fb_par *par);
56static void w100_init_graphic_engine(struct w100fb_par *par);
57struct w100_pll_info *w100_get_xtal_table(unsigned int freq);
58
59/* Pseudo palette size */
60#define MAX_PALETTES      16
61
62#define W100_SUSPEND_EXTMEM 0
63#define W100_SUSPEND_ALL    1
64
65#define BITS_PER_PIXEL    16
66
67/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
68static void *remapped_base;
69static void *remapped_regs;
70static void *remapped_fbuf;
71
72#define REMAPPED_FB_LEN   0x15ffff
73
74/* This is the offset in the w100's address space we map the current
75   framebuffer memory to. We use the position of external memory as
76   we can remap internal memory to there if external isn't present. */
77#define W100_FB_BASE MEM_EXT_BASE_VALUE
78
79
80/*
81 * Sysfs functions
82 */
83static ssize_t flip_show(struct device *dev, struct device_attribute *attr, char *buf)
84{
85	struct fb_info *info = dev_get_drvdata(dev);
86	struct w100fb_par *par=info->par;
87
88	return sprintf(buf, "%d\n",par->flip);
89}
90
91static ssize_t flip_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
92{
93	unsigned int flip;
94	struct fb_info *info = dev_get_drvdata(dev);
95	struct w100fb_par *par=info->par;
96
97	flip = simple_strtoul(buf, NULL, 10);
98
99	if (flip > 0)
100		par->flip = 1;
101	else
102		par->flip = 0;
103
104	w100_update_disable();
105	w100_set_dispregs(par);
106	w100_update_enable();
107
108	calc_hsync(par);
109
110	return count;
111}
112
113static DEVICE_ATTR(flip, 0644, flip_show, flip_store);
114
115static ssize_t w100fb_reg_read(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
116{
117	unsigned long regs, param;
118	regs = simple_strtoul(buf, NULL, 16);
119	param = readl(remapped_regs + regs);
120	printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
121	return count;
122}
123
124static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
125
126static ssize_t w100fb_reg_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
127{
128	unsigned long regs, param;
129	sscanf(buf, "%lx %lx", &regs, &param);
130
131	if (regs <= 0x2000) {
132		printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
133		writel(param, remapped_regs + regs);
134	}
135
136	return count;
137}
138
139static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
140
141
142static ssize_t fastpllclk_show(struct device *dev, struct device_attribute *attr, char *buf)
143{
144	struct fb_info *info = dev_get_drvdata(dev);
145	struct w100fb_par *par=info->par;
146
147	return sprintf(buf, "%d\n",par->fastpll_mode);
148}
149
150static ssize_t fastpllclk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
151{
152	struct fb_info *info = dev_get_drvdata(dev);
153	struct w100fb_par *par=info->par;
154
155	if (simple_strtoul(buf, NULL, 10) > 0) {
156		par->fastpll_mode=1;
157		printk("w100fb: Using fast system clock (if possible)\n");
158	} else {
159		par->fastpll_mode=0;
160		printk("w100fb: Using normal system clock\n");
161	}
162
163	w100_init_clocks(par);
164	calc_hsync(par);
165
166	return count;
167}
168
169static DEVICE_ATTR(fastpllclk, 0644, fastpllclk_show, fastpllclk_store);
170
171/*
172 * Some touchscreens need hsync information from the video driver to
173 * function correctly. We export it here.
174 */
175unsigned long w100fb_get_hsynclen(struct device *dev)
176{
177	struct fb_info *info = dev_get_drvdata(dev);
178	struct w100fb_par *par=info->par;
179
180	/* If display is blanked/suspended, hsync isn't active */
181	if (par->blanked)
182		return 0;
183	else
184		return par->hsync_len;
185}
186EXPORT_SYMBOL(w100fb_get_hsynclen);
187
188static void w100fb_clear_screen(struct w100fb_par *par)
189{
190	memset_io(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), 0, (par->xres * par->yres * BITS_PER_PIXEL/8));
191}
192
193
194/*
195 * Set a palette value from rgb components
196 */
197static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
198			     u_int trans, struct fb_info *info)
199{
200	unsigned int val;
201	int ret = 1;
202
203	/*
204	 * If greyscale is true, then we convert the RGB value
205	 * to greyscale no matter what visual we are using.
206	 */
207	if (info->var.grayscale)
208		red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
209
210	/*
211	 * 16-bit True Colour.  We encode the RGB value
212	 * according to the RGB bitfield information.
213	 */
214	if (regno < MAX_PALETTES) {
215		u32 *pal = info->pseudo_palette;
216
217		val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
218		pal[regno] = val;
219		ret = 0;
220	}
221	return ret;
222}
223
224
225/*
226 * Blank the display based on value in blank_mode
227 */
228static int w100fb_blank(int blank_mode, struct fb_info *info)
229{
230	struct w100fb_par *par = info->par;
231	struct w100_tg_info *tg = par->mach->tg;
232
233	switch(blank_mode) {
234
235 	case FB_BLANK_NORMAL:         /* Normal blanking */
236	case FB_BLANK_VSYNC_SUSPEND:  /* VESA blank (vsync off) */
237	case FB_BLANK_HSYNC_SUSPEND:  /* VESA blank (hsync off) */
238 	case FB_BLANK_POWERDOWN:      /* Poweroff */
239  		if (par->blanked == 0) {
240			if(tg && tg->suspend)
241				tg->suspend(par);
242			par->blanked = 1;
243  		}
244  		break;
245
246 	case FB_BLANK_UNBLANK: /* Unblanking */
247  		if (par->blanked != 0) {
248			if(tg && tg->resume)
249				tg->resume(par);
250			par->blanked = 0;
251  		}
252  		break;
253 	}
254	return 0;
255}
256
257
258static void w100_fifo_wait(int entries)
259{
260	union rbbm_status_u status;
261	int i;
262
263	for (i = 0; i < 2000000; i++) {
264		status.val = readl(remapped_regs + mmRBBM_STATUS);
265		if (status.f.cmdfifo_avail >= entries)
266			return;
267		udelay(1);
268	}
269	printk(KERN_ERR "w100fb: FIFO Timeout!\n");
270}
271
272
273static int w100fb_sync(struct fb_info *info)
274{
275	union rbbm_status_u status;
276	int i;
277
278	for (i = 0; i < 2000000; i++) {
279		status.val = readl(remapped_regs + mmRBBM_STATUS);
280		if (!status.f.gui_active)
281			return 0;
282		udelay(1);
283	}
284	printk(KERN_ERR "w100fb: Graphic engine timeout!\n");
285	return -EBUSY;
286}
287
288
289static void w100_init_graphic_engine(struct w100fb_par *par)
290{
291	union dp_gui_master_cntl_u gmc;
292	union dp_mix_u dp_mix;
293	union dp_datatype_u dp_datatype;
294	union dp_cntl_u dp_cntl;
295
296	w100_fifo_wait(4);
297	writel(W100_FB_BASE, remapped_regs + mmDST_OFFSET);
298	writel(par->xres, remapped_regs + mmDST_PITCH);
299	writel(W100_FB_BASE, remapped_regs + mmSRC_OFFSET);
300	writel(par->xres, remapped_regs + mmSRC_PITCH);
301
302	w100_fifo_wait(3);
303	writel(0, remapped_regs + mmSC_TOP_LEFT);
304	writel((par->yres << 16) | par->xres, remapped_regs + mmSC_BOTTOM_RIGHT);
305	writel(0x1fff1fff, remapped_regs + mmSRC_SC_BOTTOM_RIGHT);
306
307	w100_fifo_wait(4);
308	dp_cntl.val = 0;
309	dp_cntl.f.dst_x_dir = 1;
310	dp_cntl.f.dst_y_dir = 1;
311	dp_cntl.f.src_x_dir = 1;
312	dp_cntl.f.src_y_dir = 1;
313	dp_cntl.f.dst_major_x = 1;
314	dp_cntl.f.src_major_x = 1;
315	writel(dp_cntl.val, remapped_regs + mmDP_CNTL);
316
317	gmc.val = 0;
318	gmc.f.gmc_src_pitch_offset_cntl = 1;
319	gmc.f.gmc_dst_pitch_offset_cntl = 1;
320	gmc.f.gmc_src_clipping = 1;
321	gmc.f.gmc_dst_clipping = 1;
322	gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
323	gmc.f.gmc_dst_datatype = 3; /* from DstType_16Bpp_444 */
324	gmc.f.gmc_src_datatype = SRC_DATATYPE_EQU_DST;
325	gmc.f.gmc_byte_pix_order = 1;
326	gmc.f.gmc_default_sel = 0;
327	gmc.f.gmc_rop3 = ROP3_SRCCOPY;
328	gmc.f.gmc_dp_src_source = DP_SRC_MEM_RECTANGULAR;
329	gmc.f.gmc_clr_cmp_fcn_dis = 1;
330	gmc.f.gmc_wr_msk_dis = 1;
331	gmc.f.gmc_dp_op = DP_OP_ROP;
332	writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
333
334	dp_datatype.val = dp_mix.val = 0;
335	dp_datatype.f.dp_dst_datatype = gmc.f.gmc_dst_datatype;
336	dp_datatype.f.dp_brush_datatype = gmc.f.gmc_brush_datatype;
337	dp_datatype.f.dp_src2_type = 0;
338	dp_datatype.f.dp_src2_datatype = gmc.f.gmc_src_datatype;
339	dp_datatype.f.dp_src_datatype = gmc.f.gmc_src_datatype;
340	dp_datatype.f.dp_byte_pix_order = gmc.f.gmc_byte_pix_order;
341	writel(dp_datatype.val, remapped_regs + mmDP_DATATYPE);
342
343	dp_mix.f.dp_src_source = gmc.f.gmc_dp_src_source;
344	dp_mix.f.dp_src2_source = 1;
345	dp_mix.f.dp_rop3 = gmc.f.gmc_rop3;
346	dp_mix.f.dp_op = gmc.f.gmc_dp_op;
347	writel(dp_mix.val, remapped_regs + mmDP_MIX);
348}
349
350
351static void w100fb_fillrect(struct fb_info *info,
352                            const struct fb_fillrect *rect)
353{
354	union dp_gui_master_cntl_u gmc;
355
356	if (info->state != FBINFO_STATE_RUNNING)
357		return;
358	if (info->flags & FBINFO_HWACCEL_DISABLED) {
359		cfb_fillrect(info, rect);
360		return;
361	}
362
363	gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
364	gmc.f.gmc_rop3 = ROP3_PATCOPY;
365	gmc.f.gmc_brush_datatype = GMC_BRUSH_SOLID_COLOR;
366	w100_fifo_wait(2);
367	writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
368	writel(rect->color, remapped_regs + mmDP_BRUSH_FRGD_CLR);
369
370	w100_fifo_wait(2);
371	writel((rect->dy << 16) | (rect->dx & 0xffff), remapped_regs + mmDST_Y_X);
372	writel((rect->width << 16) | (rect->height & 0xffff),
373	       remapped_regs + mmDST_WIDTH_HEIGHT);
374}
375
376
377static void w100fb_copyarea(struct fb_info *info,
378                            const struct fb_copyarea *area)
379{
380	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
381	u32 h = area->height, w = area->width;
382	union dp_gui_master_cntl_u gmc;
383
384	if (info->state != FBINFO_STATE_RUNNING)
385		return;
386	if (info->flags & FBINFO_HWACCEL_DISABLED) {
387		cfb_copyarea(info, area);
388		return;
389	}
390
391	gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
392	gmc.f.gmc_rop3 = ROP3_SRCCOPY;
393	gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
394	w100_fifo_wait(1);
395	writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
396
397	w100_fifo_wait(3);
398	writel((sy << 16) | (sx & 0xffff), remapped_regs + mmSRC_Y_X);
399	writel((dy << 16) | (dx & 0xffff), remapped_regs + mmDST_Y_X);
400	writel((w << 16) | (h & 0xffff), remapped_regs + mmDST_WIDTH_HEIGHT);
401}
402
403
404/*
405 *  Change the resolution by calling the appropriate hardware functions
406 */
407static void w100fb_activate_var(struct w100fb_par *par)
408{
409	struct w100_tg_info *tg = par->mach->tg;
410
411	w100_pwm_setup(par);
412	w100_setup_memory(par);
413	w100_init_clocks(par);
414	w100fb_clear_screen(par);
415	w100_vsync();
416
417	w100_update_disable();
418	w100_init_lcd(par);
419	w100_set_dispregs(par);
420	w100_update_enable();
421	w100_init_graphic_engine(par);
422
423	calc_hsync(par);
424
425	if (!par->blanked && tg && tg->change)
426		tg->change(par);
427}
428
429
430/* Select the smallest mode that allows the desired resolution to be
431 * displayed. If desired, the x and y parameters can be rounded up to
432 * match the selected mode.
433 */
434static struct w100_mode *w100fb_get_mode(struct w100fb_par *par, unsigned int *x, unsigned int *y, int saveval)
435{
436	struct w100_mode *mode = NULL;
437	struct w100_mode *modelist = par->mach->modelist;
438	unsigned int best_x = 0xffffffff, best_y = 0xffffffff;
439	unsigned int i;
440
441	for (i = 0 ; i < par->mach->num_modes ; i++) {
442		if (modelist[i].xres >= *x && modelist[i].yres >= *y &&
443				modelist[i].xres < best_x && modelist[i].yres < best_y) {
444			best_x = modelist[i].xres;
445			best_y = modelist[i].yres;
446			mode = &modelist[i];
447		} else if(modelist[i].xres >= *y && modelist[i].yres >= *x &&
448		        modelist[i].xres < best_y && modelist[i].yres < best_x) {
449			best_x = modelist[i].yres;
450			best_y = modelist[i].xres;
451			mode = &modelist[i];
452		}
453	}
454
455	if (mode && saveval) {
456		*x = best_x;
457		*y = best_y;
458	}
459
460	return mode;
461}
462
463
464/*
465 *  w100fb_check_var():
466 *  Get the video params out of 'var'. If a value doesn't fit, round it up,
467 *  if it's too big, return -EINVAL.
468 */
469static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
470{
471	struct w100fb_par *par=info->par;
472
473	if(!w100fb_get_mode(par, &var->xres, &var->yres, 1))
474		return -EINVAL;
475
476	if (par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (par->mach->mem->size+1)))
477		return -EINVAL;
478
479	if (!par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)))
480		return -EINVAL;
481
482	var->xres_virtual = max(var->xres_virtual, var->xres);
483	var->yres_virtual = max(var->yres_virtual, var->yres);
484
485	if (var->bits_per_pixel > BITS_PER_PIXEL)
486		return -EINVAL;
487	else
488		var->bits_per_pixel = BITS_PER_PIXEL;
489
490	var->red.offset = 11;
491	var->red.length = 5;
492	var->green.offset = 5;
493	var->green.length = 6;
494	var->blue.offset = 0;
495	var->blue.length = 5;
496	var->transp.offset = var->transp.length = 0;
497
498	var->nonstd = 0;
499	var->height = -1;
500	var->width = -1;
501	var->vmode = FB_VMODE_NONINTERLACED;
502	var->sync = 0;
503	var->pixclock = 0x04;  /* 171521; */
504
505	return 0;
506}
507
508
509/*
510 * w100fb_set_par():
511 *	Set the user defined part of the display for the specified console
512 *  by looking at the values in info.var
513 */
514static int w100fb_set_par(struct fb_info *info)
515{
516	struct w100fb_par *par=info->par;
517
518	if (par->xres != info->var.xres || par->yres != info->var.yres)	{
519		par->xres = info->var.xres;
520		par->yres = info->var.yres;
521		par->mode = w100fb_get_mode(par, &par->xres, &par->yres, 0);
522
523		info->fix.visual = FB_VISUAL_TRUECOLOR;
524		info->fix.ypanstep = 0;
525		info->fix.ywrapstep = 0;
526		info->fix.line_length = par->xres * BITS_PER_PIXEL / 8;
527
528		mutex_lock(&info->mm_lock);
529		if ((par->xres*par->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)) {
530			par->extmem_active = 1;
531			info->fix.smem_len = par->mach->mem->size+1;
532		} else {
533			par->extmem_active = 0;
534			info->fix.smem_len = MEM_INT_SIZE+1;
535		}
536		mutex_unlock(&info->mm_lock);
537
538		w100fb_activate_var(par);
539	}
540	return 0;
541}
542
543
544/*
545 *  Frame buffer operations
546 */
547static struct fb_ops w100fb_ops = {
548	.owner        = THIS_MODULE,
549	.fb_check_var = w100fb_check_var,
550	.fb_set_par   = w100fb_set_par,
551	.fb_setcolreg = w100fb_setcolreg,
552	.fb_blank     = w100fb_blank,
553	.fb_fillrect  = w100fb_fillrect,
554	.fb_copyarea  = w100fb_copyarea,
555	.fb_imageblit = cfb_imageblit,
556	.fb_sync      = w100fb_sync,
557};
558
559#ifdef CONFIG_PM
560static void w100fb_save_vidmem(struct w100fb_par *par)
561{
562	int memsize;
563
564	if (par->extmem_active) {
565		memsize=par->mach->mem->size;
566		par->saved_extmem = vmalloc(memsize);
567		if (par->saved_extmem)
568			memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
569	}
570	memsize=MEM_INT_SIZE;
571	par->saved_intmem = vmalloc(memsize);
572	if (par->saved_intmem && par->extmem_active)
573		memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
574	else if (par->saved_intmem)
575		memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
576}
577
578static void w100fb_restore_vidmem(struct w100fb_par *par)
579{
580	int memsize;
581
582	if (par->extmem_active && par->saved_extmem) {
583		memsize=par->mach->mem->size;
584		memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
585		vfree(par->saved_extmem);
586	}
587	if (par->saved_intmem) {
588		memsize=MEM_INT_SIZE;
589		if (par->extmem_active)
590			memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
591		else
592			memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
593		vfree(par->saved_intmem);
594	}
595}
596
597static int w100fb_suspend(struct platform_device *dev, pm_message_t state)
598{
599	struct fb_info *info = platform_get_drvdata(dev);
600	struct w100fb_par *par=info->par;
601	struct w100_tg_info *tg = par->mach->tg;
602
603	w100fb_save_vidmem(par);
604	if(tg && tg->suspend)
605		tg->suspend(par);
606	w100_suspend(W100_SUSPEND_ALL);
607	par->blanked = 1;
608
609	return 0;
610}
611
612static int w100fb_resume(struct platform_device *dev)
613{
614	struct fb_info *info = platform_get_drvdata(dev);
615	struct w100fb_par *par=info->par;
616	struct w100_tg_info *tg = par->mach->tg;
617
618	w100_hw_init(par);
619	w100fb_activate_var(par);
620	w100fb_restore_vidmem(par);
621	if(tg && tg->resume)
622		tg->resume(par);
623	par->blanked = 0;
624
625	return 0;
626}
627#else
628#define w100fb_suspend  NULL
629#define w100fb_resume   NULL
630#endif
631
632
633int w100fb_probe(struct platform_device *pdev)
634{
635	int err = -EIO;
636	struct w100fb_mach_info *inf;
637	struct fb_info *info = NULL;
638	struct w100fb_par *par;
639	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
640	unsigned int chip_id;
641
642	if (!mem)
643		return -EINVAL;
644
645	/* Remap the chip base address */
646	remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
647	if (remapped_base == NULL)
648		goto out;
649
650	/* Map the register space */
651	remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
652	if (remapped_regs == NULL)
653		goto out;
654
655	/* Identify the chip */
656	printk("Found ");
657	chip_id = readl(remapped_regs + mmCHIP_ID);
658	switch(chip_id) {
659		case CHIP_ID_W100:  printk("w100");  break;
660		case CHIP_ID_W3200: printk("w3200"); break;
661		case CHIP_ID_W3220: printk("w3220"); break;
662		default:
663			printk("Unknown imageon chip ID\n");
664			err = -ENODEV;
665			goto out;
666	}
667	printk(" at 0x%08lx.\n", (unsigned long) mem->start+W100_CFG_BASE);
668
669	/* Remap the framebuffer */
670	remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
671	if (remapped_fbuf == NULL)
672		goto out;
673
674	info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
675	if (!info) {
676		err = -ENOMEM;
677		goto out;
678	}
679
680	par = info->par;
681	platform_set_drvdata(pdev, info);
682
683	inf = dev_get_platdata(&pdev->dev);
684	par->chip_id = chip_id;
685	par->mach = inf;
686	par->fastpll_mode = 0;
687	par->blanked = 0;
688
689	par->pll_table=w100_get_xtal_table(inf->xtal_freq);
690	if (!par->pll_table) {
691		printk(KERN_ERR "No matching Xtal definition found\n");
692		err = -EINVAL;
693		goto out;
694	}
695
696	info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
697	if (!info->pseudo_palette) {
698		err = -ENOMEM;
699		goto out;
700	}
701
702	info->fbops = &w100fb_ops;
703	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
704		FBINFO_HWACCEL_FILLRECT;
705	info->node = -1;
706	info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
707	info->screen_size = REMAPPED_FB_LEN;
708
709	strcpy(info->fix.id, "w100fb");
710	info->fix.type = FB_TYPE_PACKED_PIXELS;
711	info->fix.type_aux = 0;
712	info->fix.accel = FB_ACCEL_NONE;
713	info->fix.smem_start = mem->start+W100_FB_BASE;
714	info->fix.mmio_start = mem->start+W100_REG_BASE;
715	info->fix.mmio_len = W100_REG_LEN;
716
717	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
718		err = -ENOMEM;
719		goto out;
720	}
721
722	par->mode = &inf->modelist[0];
723	if(inf->init_mode & INIT_MODE_ROTATED) {
724		info->var.xres = par->mode->yres;
725		info->var.yres = par->mode->xres;
726	}
727	else {
728		info->var.xres = par->mode->xres;
729		info->var.yres = par->mode->yres;
730	}
731
732	if(inf->init_mode &= INIT_MODE_FLIPPED)
733		par->flip = 1;
734	else
735		par->flip = 0;
736
737	info->var.xres_virtual = info->var.xres;
738	info->var.yres_virtual = info->var.yres;
739	info->var.pixclock = 0x04;  /* 171521; */
740	info->var.sync = 0;
741	info->var.grayscale = 0;
742	info->var.xoffset = info->var.yoffset = 0;
743	info->var.accel_flags = 0;
744	info->var.activate = FB_ACTIVATE_NOW;
745
746	w100_hw_init(par);
747
748	if (w100fb_check_var(&info->var, info) < 0) {
749		err = -EINVAL;
750		goto out;
751	}
752
753	if (register_framebuffer(info) < 0) {
754		err = -EINVAL;
755		goto out;
756	}
757
758	err = device_create_file(&pdev->dev, &dev_attr_fastpllclk);
759	err |= device_create_file(&pdev->dev, &dev_attr_reg_read);
760	err |= device_create_file(&pdev->dev, &dev_attr_reg_write);
761	err |= device_create_file(&pdev->dev, &dev_attr_flip);
762
763	if (err != 0)
764		fb_warn(info, "failed to register attributes (%d)\n", err);
765
766	fb_info(info, "%s frame buffer device\n", info->fix.id);
767	return 0;
768out:
769	if (info) {
770		fb_dealloc_cmap(&info->cmap);
771		kfree(info->pseudo_palette);
772	}
773	if (remapped_fbuf != NULL)
774		iounmap(remapped_fbuf);
775	if (remapped_regs != NULL)
776		iounmap(remapped_regs);
777	if (remapped_base != NULL)
778		iounmap(remapped_base);
779	if (info)
780		framebuffer_release(info);
781	return err;
782}
783
784
785static int w100fb_remove(struct platform_device *pdev)
786{
787	struct fb_info *info = platform_get_drvdata(pdev);
788	struct w100fb_par *par=info->par;
789
790	device_remove_file(&pdev->dev, &dev_attr_fastpllclk);
791	device_remove_file(&pdev->dev, &dev_attr_reg_read);
792	device_remove_file(&pdev->dev, &dev_attr_reg_write);
793	device_remove_file(&pdev->dev, &dev_attr_flip);
794
795	unregister_framebuffer(info);
796
797	vfree(par->saved_intmem);
798	vfree(par->saved_extmem);
799	kfree(info->pseudo_palette);
800	fb_dealloc_cmap(&info->cmap);
801
802	iounmap(remapped_base);
803	iounmap(remapped_regs);
804	iounmap(remapped_fbuf);
805
806	framebuffer_release(info);
807
808	return 0;
809}
810
811
812/* ------------------- chipset specific functions -------------------------- */
813
814
815static void w100_soft_reset(void)
816{
817	u16 val = readw((u16 *) remapped_base + cfgSTATUS);
818	writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
819	udelay(100);
820	writew(0x00, (u16 *) remapped_base + cfgSTATUS);
821	udelay(100);
822}
823
824static void w100_update_disable(void)
825{
826	union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
827
828	/* Prevent display updates */
829	disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
830	disp_db_buf_wr_cntl.f.update_db_buf = 0;
831	disp_db_buf_wr_cntl.f.en_db_buf = 0;
832	writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
833}
834
835static void w100_update_enable(void)
836{
837	union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
838
839	/* Enable display updates */
840	disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
841	disp_db_buf_wr_cntl.f.update_db_buf = 1;
842	disp_db_buf_wr_cntl.f.en_db_buf = 1;
843	writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
844}
845
846unsigned long w100fb_gpio_read(int port)
847{
848	unsigned long value;
849
850	if (port==W100_GPIO_PORT_A)
851		value = readl(remapped_regs + mmGPIO_DATA);
852	else
853		value = readl(remapped_regs + mmGPIO_DATA2);
854
855	return value;
856}
857
858void w100fb_gpio_write(int port, unsigned long value)
859{
860	if (port==W100_GPIO_PORT_A)
861		writel(value, remapped_regs + mmGPIO_DATA);
862	else
863		writel(value, remapped_regs + mmGPIO_DATA2);
864}
865EXPORT_SYMBOL(w100fb_gpio_read);
866EXPORT_SYMBOL(w100fb_gpio_write);
867
868/*
869 * Initialization of critical w100 hardware
870 */
871static void w100_hw_init(struct w100fb_par *par)
872{
873	u32 temp32;
874	union cif_cntl_u cif_cntl;
875	union intf_cntl_u intf_cntl;
876	union cfgreg_base_u cfgreg_base;
877	union wrap_top_dir_u wrap_top_dir;
878	union cif_read_dbg_u cif_read_dbg;
879	union cpu_defaults_u cpu_default;
880	union cif_write_dbg_u cif_write_dbg;
881	union wrap_start_dir_u wrap_start_dir;
882	union cif_io_u cif_io;
883	struct w100_gpio_regs *gpio = par->mach->gpio;
884
885	w100_soft_reset();
886
887	/* This is what the fpga_init code does on reset. May be wrong
888	   but there is little info available */
889	writel(0x31, remapped_regs + mmSCRATCH_UMSK);
890	for (temp32 = 0; temp32 < 10000; temp32++)
891		readl(remapped_regs + mmSCRATCH_UMSK);
892	writel(0x30, remapped_regs + mmSCRATCH_UMSK);
893
894	/* Set up CIF */
895	cif_io.val = defCIF_IO;
896	writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
897
898	cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
899	cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
900	cif_write_dbg.f.en_dword_split_to_rbbm = 1;
901	cif_write_dbg.f.dis_timeout_during_rbbm = 1;
902	writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
903
904	cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
905	cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
906	writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
907
908	cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
909	cif_cntl.f.dis_system_bits = 1;
910	cif_cntl.f.dis_mr = 1;
911	cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
912	cif_cntl.f.intb_oe = 1;
913	cif_cntl.f.interrupt_active_high = 1;
914	writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
915
916	/* Setup cfgINTF_CNTL and cfgCPU defaults */
917	intf_cntl.val = defINTF_CNTL;
918	intf_cntl.f.ad_inc_a = 1;
919	intf_cntl.f.ad_inc_b = 1;
920	intf_cntl.f.rd_data_rdy_a = 0;
921	intf_cntl.f.rd_data_rdy_b = 0;
922	writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
923
924	cpu_default.val = defCPU_DEFAULTS;
925	cpu_default.f.access_ind_addr_a = 1;
926	cpu_default.f.access_ind_addr_b = 1;
927	cpu_default.f.access_scratch_reg = 1;
928	cpu_default.f.transition_size = 0;
929	writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
930
931	/* set up the apertures */
932	writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
933
934	cfgreg_base.val = defCFGREG_BASE;
935	cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
936	writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
937
938	wrap_start_dir.val = defWRAP_START_DIR;
939	wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
940	writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
941
942	wrap_top_dir.val = defWRAP_TOP_DIR;
943	wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
944	writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
945
946	writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
947
948	/* Set the hardware to 565 colour */
949	temp32 = readl(remapped_regs + mmDISP_DEBUG2);
950	temp32 &= 0xff7fffff;
951	temp32 |= 0x00800000;
952	writel(temp32, remapped_regs + mmDISP_DEBUG2);
953
954	/* Initialise the GPIO lines */
955	if (gpio) {
956		writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
957		writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
958		writel(gpio->gpio_dir1,  remapped_regs + mmGPIO_CNTL1);
959		writel(gpio->gpio_oe1,   remapped_regs + mmGPIO_CNTL2);
960		writel(gpio->gpio_dir2,  remapped_regs + mmGPIO_CNTL3);
961		writel(gpio->gpio_oe2,   remapped_regs + mmGPIO_CNTL4);
962	}
963}
964
965
966struct power_state {
967	union clk_pin_cntl_u clk_pin_cntl;
968	union pll_ref_fb_div_u pll_ref_fb_div;
969	union pll_cntl_u pll_cntl;
970	union sclk_cntl_u sclk_cntl;
971	union pclk_cntl_u pclk_cntl;
972	union pwrmgt_cntl_u pwrmgt_cntl;
973	int auto_mode;  /* system clock auto changing? */
974};
975
976
977static struct power_state w100_pwr_state;
978
979/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
980
981/* 12.5MHz Crystal PLL Table */
982static struct w100_pll_info xtal_12500000[] = {
983	/*freq     M   N_int    N_fac  tfgoal  lock_time */
984	{ 50,      0,   1,       0,     0xe0,        56},  /*  50.00 MHz */
985	{ 75,      0,   5,       0,     0xde,        37},  /*  75.00 MHz */
986	{100,      0,   7,       0,     0xe0,        28},  /* 100.00 MHz */
987	{125,      0,   9,       0,     0xe0,        22},  /* 125.00 MHz */
988	{150,      0,   11,      0,     0xe0,        17},  /* 150.00 MHz */
989	{  0,      0,   0,       0,        0,         0},  /* Terminator */
990};
991
992/* 14.318MHz Crystal PLL Table */
993static struct w100_pll_info xtal_14318000[] = {
994	/*freq     M   N_int    N_fac  tfgoal  lock_time */
995	{ 40,      4,   13,      0,     0xe0,        80}, /* tfgoal guessed */
996	{ 50,      1,   6,       0,     0xe0,	     64}, /*  50.05 MHz */
997	{ 57,      2,   11,      0,     0xe0,        53}, /* tfgoal guessed */
998	{ 75,      0,   4,       3,     0xe0,	     43}, /*  75.08 MHz */
999	{100,      0,   6,       0,     0xe0,        32}, /* 100.10 MHz */
1000	{  0,      0,   0,       0,        0,         0},
1001};
1002
1003/* 16MHz Crystal PLL Table */
1004static struct w100_pll_info xtal_16000000[] = {
1005	/*freq     M   N_int    N_fac  tfgoal  lock_time */
1006	{ 72,      1,   8,       0,     0xe0,        48}, /* tfgoal guessed */
1007	{ 80,      1,   9,       0,     0xe0,        13}, /* tfgoal guessed */
1008	{ 95,      1,   10,      7,     0xe0,        38}, /* tfgoal guessed */
1009	{ 96,      1,   11,      0,     0xe0,        36}, /* tfgoal guessed */
1010	{  0,      0,   0,       0,        0,         0},
1011};
1012
1013static struct pll_entries {
1014	int xtal_freq;
1015	struct w100_pll_info *pll_table;
1016} w100_pll_tables[] = {
1017	{ 12500000, &xtal_12500000[0] },
1018	{ 14318000, &xtal_14318000[0] },
1019	{ 16000000, &xtal_16000000[0] },
1020	{ 0 },
1021};
1022
1023struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
1024{
1025	struct pll_entries *pll_entry = w100_pll_tables;
1026
1027	do {
1028		if (freq == pll_entry->xtal_freq)
1029			return pll_entry->pll_table;
1030		pll_entry++;
1031	} while (pll_entry->xtal_freq);
1032	return 0;
1033}
1034
1035
1036static unsigned int w100_get_testcount(unsigned int testclk_sel)
1037{
1038	union clk_test_cntl_u clk_test_cntl;
1039
1040	udelay(5);
1041
1042	/* Select the test clock source and reset */
1043	clk_test_cntl.f.start_check_freq = 0x0;
1044	clk_test_cntl.f.testclk_sel = testclk_sel;
1045	clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
1046	writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1047
1048	clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
1049	writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1050
1051	/* Run clock test */
1052	clk_test_cntl.f.start_check_freq = 0x1;
1053	writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1054
1055	/* Give the test time to complete */
1056	udelay(20);
1057
1058	/* Return the result */
1059	clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
1060	clk_test_cntl.f.start_check_freq = 0x0;
1061	writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1062
1063	return clk_test_cntl.f.test_count;
1064}
1065
1066
1067static int w100_pll_adjust(struct w100_pll_info *pll)
1068{
1069	unsigned int tf80;
1070	unsigned int tf20;
1071
1072	/* Initial Settings */
1073	w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0;     /* power down */
1074	w100_pwr_state.pll_cntl.f.pll_reset = 0x0;    /* not reset */
1075	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1;   /* Hi-Z */
1076	w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;      /* VCO gain = 0 */
1077	w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;    /* VCO frequency range control = off */
1078	w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;  /* current offset inside VCO = 0 */
1079	w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1080
1081	/* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
1082	 * therefore, commented out the following lines
1083	 * tf80 meant tf100
1084	 */
1085	do {
1086		/* set VCO input = 0.8 * VDD */
1087		w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
1088		writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1089
1090		tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
1091		if (tf80 >= (pll->tfgoal)) {
1092			/* set VCO input = 0.2 * VDD */
1093			w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
1094			writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1095
1096			tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
1097			if (tf20 <= (pll->tfgoal))
1098				return 1;  /* Success */
1099
1100			if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
1101				((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
1102				(w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
1103				/* slow VCO config */
1104				w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
1105				w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1106				w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1107				continue;
1108			}
1109		}
1110		if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
1111			w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
1112		} else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
1113			w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1114			w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
1115		} else {
1116			return 0;  /* Error */
1117		}
1118	} while(1);
1119}
1120
1121
1122/*
1123 * w100_pll_calibration
1124 */
1125static int w100_pll_calibration(struct w100_pll_info *pll)
1126{
1127	int status;
1128
1129	status = w100_pll_adjust(pll);
1130
1131	/* PLL Reset And Lock */
1132	/* set VCO input = 0.5 * VDD */
1133	w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
1134	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1135
1136	udelay(1);  /* reset time */
1137
1138	/* enable charge pump */
1139	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;  /* normal */
1140	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1141
1142	/* set VCO input = Hi-Z, disable DAC */
1143	w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
1144	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1145
1146	udelay(400);  /* lock time */
1147
1148	/* PLL locked */
1149
1150	return status;
1151}
1152
1153
1154static int w100_pll_set_clk(struct w100_pll_info *pll)
1155{
1156	int status;
1157
1158	if (w100_pwr_state.auto_mode == 1)  /* auto mode */
1159	{
1160		w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;  /* disable fast to normal */
1161		w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;  /* disable normal to fast */
1162		writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1163	}
1164
1165	/* Set system clock source to XTAL whilst adjusting the PLL! */
1166	w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1167	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1168
1169	w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1170	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1171	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1172	w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
1173	writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1174
1175	w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1176	writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1177
1178	status = w100_pll_calibration(pll);
1179
1180	if (w100_pwr_state.auto_mode == 1)  /* auto mode */
1181	{
1182		w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1;  /* reenable fast to normal */
1183		w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1;  /* reenable normal to fast  */
1184		writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1185	}
1186	return status;
1187}
1188
1189/* freq = target frequency of the PLL */
1190static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
1191{
1192	struct w100_pll_info *pll = par->pll_table;
1193
1194	do {
1195		if (freq == pll->freq) {
1196			return w100_pll_set_clk(pll);
1197		}
1198		pll++;
1199	} while(pll->freq);
1200	return 0;
1201}
1202
1203/* Set up an initial state.  Some values/fields set
1204   here will be overwritten. */
1205static void w100_pwm_setup(struct w100fb_par *par)
1206{
1207	w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1208	w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1209	w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1210	w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
1211	w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
1212	w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1213	writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1214
1215	w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1216	w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0;  /* Pfast = 1 */
1217	w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
1218	w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0;  /* Pslow = 1 */
1219	w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
1220	w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0;    /* Dynamic */
1221	w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0;   /* Dynamic */
1222	w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0;     /* Dynamic */
1223	w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0;  /* Dynamic */
1224	w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0;     /* Dynamic */
1225	w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0;     /* Dynamic */
1226	w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0;     /* Dynamic */
1227	w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0;   /* Dynamic */
1228	w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0;   /* Dynamic */
1229	w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1230	w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1231	w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1232	w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1233	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1234
1235	w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1236	w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1;    /* P = 2 */
1237	w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0;  /* Dynamic */
1238	writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1239
1240	w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0;     /* M = 1 */
1241	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0;  /* N = 1.0 */
1242	w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1243	w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1244	w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1245	writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1246
1247	w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1248	w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1249	w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
1250	w100_pwr_state.pll_cntl.f.pll_mode = 0x0;  /* uses VCO clock */
1251	w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1252	w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1253	w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1254	w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1255	w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1256	w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1257	w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1258	w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1259	w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
1260	w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;  /* Hi-Z */
1261	w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1262	w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1263	w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1264	w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1265	writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1266
1267	w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
1268	w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;  /* normal mode (0, 1, 3) */
1269	w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1270	w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1271	w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
1272	w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1;  /* PM4,ENG */
1273	w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1;  /* PM4,ENG */
1274	w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1275	w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1276	writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1277
1278	w100_pwr_state.auto_mode = 0;  /* manual mode */
1279}
1280
1281
1282/*
1283 * Setup the w100 clocks for the specified mode
1284 */
1285static void w100_init_clocks(struct w100fb_par *par)
1286{
1287	struct w100_mode *mode = par->mode;
1288
1289	if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1290		w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1291
1292	w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1293	w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1294	w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1295	writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1296}
1297
1298static void w100_init_lcd(struct w100fb_par *par)
1299{
1300	u32 temp32;
1301	struct w100_mode *mode = par->mode;
1302	struct w100_gen_regs *regs = par->mach->regs;
1303	union active_h_disp_u active_h_disp;
1304	union active_v_disp_u active_v_disp;
1305	union graphic_h_disp_u graphic_h_disp;
1306	union graphic_v_disp_u graphic_v_disp;
1307	union crtc_total_u crtc_total;
1308
1309	/* w3200 doesn't like undefined bits being set so zero register values first */
1310
1311	active_h_disp.val = 0;
1312	active_h_disp.f.active_h_start=mode->left_margin;
1313	active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1314	writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
1315
1316	active_v_disp.val = 0;
1317	active_v_disp.f.active_v_start=mode->upper_margin;
1318	active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1319	writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
1320
1321	graphic_h_disp.val = 0;
1322	graphic_h_disp.f.graphic_h_start=mode->left_margin;
1323	graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1324	writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1325
1326	graphic_v_disp.val = 0;
1327	graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1328	graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1329	writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1330
1331	crtc_total.val = 0;
1332	crtc_total.f.crtc_h_total=mode->left_margin  + mode->xres + mode->right_margin;
1333	crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1334	writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1335
1336	writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1337	writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1338	writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1339	writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1340	writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1341	writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1342	writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1343	writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1344	writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1345
1346	writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1347	writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1348	writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1349	writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1350	writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1351	writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1352
1353	writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1354	writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1355	writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1356	writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
1357
1358	/* Hack for overlay in ext memory */
1359	temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1360	temp32 |= 0xc0000000;
1361	writel(temp32, remapped_regs + mmDISP_DEBUG2);
1362}
1363
1364
1365static void w100_setup_memory(struct w100fb_par *par)
1366{
1367	union mc_ext_mem_location_u extmem_location;
1368	union mc_fb_location_u intmem_location;
1369	struct w100_mem_info *mem = par->mach->mem;
1370	struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1371
1372	if (!par->extmem_active) {
1373		w100_suspend(W100_SUSPEND_EXTMEM);
1374
1375		/* Map Internal Memory at FB Base */
1376		intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1377		intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1378		writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1379
1380		/* Unmap External Memory - value is *probably* irrelevant but may have meaning
1381		   to acceleration libraries */
1382		extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1383		extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1384		writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1385	} else {
1386		/* Map Internal Memory to its default location */
1387		intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1388		intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1389		writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1390
1391		/* Map External Memory at FB Base */
1392		extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1393		extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1394		writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1395
1396		writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1397		writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1398		writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1399		udelay(100);
1400		writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1401		udelay(100);
1402		writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1403		udelay(100);
1404		writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1405		writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1406		if (bm_mem) {
1407			writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1408			writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1409			writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1410			writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1411			writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1412			writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1413			writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1414		}
1415	}
1416}
1417
1418static void w100_set_dispregs(struct w100fb_par *par)
1419{
1420	unsigned long rot=0, divider, offset=0;
1421	union graphic_ctrl_u graphic_ctrl;
1422
1423	/* See if the mode has been rotated */
1424	if (par->xres == par->mode->xres) {
1425		if (par->flip) {
1426			rot=3; /* 180 degree */
1427			offset=(par->xres * par->yres) - 1;
1428		} /* else 0 degree */
1429		divider = par->mode->pixclk_divider;
1430	} else {
1431		if (par->flip) {
1432			rot=2; /* 270 degree */
1433			offset=par->xres - 1;
1434		} else {
1435			rot=1; /* 90 degree */
1436			offset=par->xres * (par->yres - 1);
1437		}
1438		divider = par->mode->pixclk_divider_rotated;
1439	}
1440
1441	graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1442	switch (par->chip_id) {
1443		case CHIP_ID_W100:
1444			graphic_ctrl.f_w100.color_depth=6;
1445			graphic_ctrl.f_w100.en_crtc=1;
1446			graphic_ctrl.f_w100.en_graphic_req=1;
1447			graphic_ctrl.f_w100.en_graphic_crtc=1;
1448			graphic_ctrl.f_w100.lcd_pclk_on=1;
1449			graphic_ctrl.f_w100.lcd_sclk_on=1;
1450			graphic_ctrl.f_w100.low_power_on=0;
1451			graphic_ctrl.f_w100.req_freq=0;
1452			graphic_ctrl.f_w100.portrait_mode=rot;
1453
1454			/* Zaurus needs this */
1455			switch(par->xres) {
1456				case 240:
1457				case 320:
1458				default:
1459					graphic_ctrl.f_w100.total_req_graphic=0xa0;
1460					break;
1461				case 480:
1462				case 640:
1463					switch(rot) {
1464						case 0:  /* 0 */
1465						case 3:  /* 180 */
1466							graphic_ctrl.f_w100.low_power_on=1;
1467							graphic_ctrl.f_w100.req_freq=5;
1468						break;
1469						case 1:  /* 90 */
1470						case 2:  /* 270 */
1471							graphic_ctrl.f_w100.req_freq=4;
1472							break;
1473						default:
1474							break;
1475					}
1476					graphic_ctrl.f_w100.total_req_graphic=0xf0;
1477					break;
1478			}
1479			break;
1480		case CHIP_ID_W3200:
1481		case CHIP_ID_W3220:
1482			graphic_ctrl.f_w32xx.color_depth=6;
1483			graphic_ctrl.f_w32xx.en_crtc=1;
1484			graphic_ctrl.f_w32xx.en_graphic_req=1;
1485			graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1486			graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1487			graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1488			graphic_ctrl.f_w32xx.low_power_on=0;
1489			graphic_ctrl.f_w32xx.req_freq=0;
1490			graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1491			graphic_ctrl.f_w32xx.portrait_mode=rot;
1492			break;
1493	}
1494
1495	/* Set the pixel clock source and divider */
1496	w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
1497	w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1498	writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1499
1500	writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1501	writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1502	writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
1503}
1504
1505
1506/*
1507 * Work out how long the sync pulse lasts
1508 * Value is 1/(time in seconds)
1509 */
1510static void calc_hsync(struct w100fb_par *par)
1511{
1512	unsigned long hsync;
1513	struct w100_mode *mode = par->mode;
1514	union crtc_ss_u crtc_ss;
1515
1516	if (mode->pixclk_src == CLK_SRC_XTAL)
1517		hsync=par->mach->xtal_freq;
1518	else
1519		hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1520
1521	hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1522
1523	crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1524	if (crtc_ss.val)
1525		par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1526	else
1527		par->hsync_len = 0;
1528}
1529
1530static void w100_suspend(u32 mode)
1531{
1532	u32 val;
1533
1534	writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1535	writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1536
1537	val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
1538	val &= ~(0x00100000);  /* bit20=0 */
1539	val |= 0xFF000000;     /* bit31:24=0xff */
1540	writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1541
1542	val = readl(remapped_regs + mmMEM_EXT_CNTL);
1543	val &= ~(0x00040000);  /* bit18=0 */
1544	val |= 0x00080000;     /* bit19=1 */
1545	writel(val, remapped_regs + mmMEM_EXT_CNTL);
1546
1547	udelay(1);  /* wait 1us */
1548
1549	if (mode == W100_SUSPEND_EXTMEM) {
1550		/* CKE: Tri-State */
1551		val = readl(remapped_regs + mmMEM_EXT_CNTL);
1552		val |= 0x40000000;  /* bit30=1 */
1553		writel(val, remapped_regs + mmMEM_EXT_CNTL);
1554
1555		/* CLK: Stop */
1556		val = readl(remapped_regs + mmMEM_EXT_CNTL);
1557		val &= ~(0x00000001);  /* bit0=0 */
1558		writel(val, remapped_regs + mmMEM_EXT_CNTL);
1559	} else {
1560		writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1561		writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1562		writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1563
1564		udelay(5);
1565
1566		val = readl(remapped_regs + mmPLL_CNTL);
1567		val |= 0x00000004;  /* bit2=1 */
1568		writel(val, remapped_regs + mmPLL_CNTL);
1569
1570		writel(0x00000000, remapped_regs + mmLCDD_CNTL1);
1571		writel(0x00000000, remapped_regs + mmLCDD_CNTL2);
1572		writel(0x00000000, remapped_regs + mmGENLCD_CNTL1);
1573		writel(0x00000000, remapped_regs + mmGENLCD_CNTL2);
1574		writel(0x00000000, remapped_regs + mmGENLCD_CNTL3);
1575
1576		val = readl(remapped_regs + mmMEM_EXT_CNTL);
1577		val |= 0xF0000000;
1578		val &= ~(0x00000001);
1579		writel(val, remapped_regs + mmMEM_EXT_CNTL);
1580
1581		writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1582	}
1583}
1584
1585static void w100_vsync(void)
1586{
1587	u32 tmp;
1588	int timeout = 30000;  /* VSync timeout = 30[ms] > 16.8[ms] */
1589
1590	tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1591
1592	/* set vline pos  */
1593	writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1594
1595	/* disable vline irq */
1596	tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1597
1598	tmp &= ~0x00000002;
1599	writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1600
1601	/* clear vline irq status */
1602	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1603
1604	/* enable vline irq */
1605	writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1606
1607	/* clear vline irq status */
1608	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1609
1610	while(timeout > 0) {
1611		if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1612			break;
1613		udelay(1);
1614		timeout--;
1615	}
1616
1617	/* disable vline irq */
1618	writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1619
1620	/* clear vline irq status */
1621	writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1622}
1623
1624static struct platform_driver w100fb_driver = {
1625	.probe		= w100fb_probe,
1626	.remove		= w100fb_remove,
1627	.suspend	= w100fb_suspend,
1628	.resume		= w100fb_resume,
1629	.driver		= {
1630		.name	= "w100fb",
1631	},
1632};
1633
1634module_platform_driver(w100fb_driver);
1635
1636MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
1637MODULE_LICENSE("GPL");
1638