blob: 0030c071da8fd5409ebb1e3276ccb09fec4bbcd3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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-2005 Richard Purdie
Richard Purdieaac51f02005-09-06 15:19:03 -07008 * Copyright (c) 2005 Ian Molton
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
10 * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
11 *
Richard Purdieaac51f02005-09-06 15:19:03 -070012 * Generic platform support by Ian Molton <spyro@f2s.com>
13 * and Richard Purdie <rpurdie@rpsys.net>
14 *
15 * w32xx support by Ian Molton
16 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License version 2 as
19 * published by the Free Software Foundation.
20 *
21 */
22
23#include <linux/delay.h>
24#include <linux/fb.h>
25#include <linux/init.h>
26#include <linux/kernel.h>
27#include <linux/mm.h>
28#include <linux/device.h>
29#include <linux/string.h>
Richard Purdieaac51f02005-09-06 15:19:03 -070030#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <asm/io.h>
32#include <asm/uaccess.h>
33#include <video/w100fb.h>
34#include "w100fb.h"
35
36/*
37 * Prototypes
38 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070039static void w100_suspend(u32 mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070040static void w100_vsync(void);
Richard Purdieaac51f02005-09-06 15:19:03 -070041static void w100_hw_init(struct w100fb_par*);
42static void w100_pwm_setup(struct w100fb_par*);
43static void w100_init_clocks(struct w100fb_par*);
44static void w100_setup_memory(struct w100fb_par*);
45static void w100_init_lcd(struct w100fb_par*);
46static void w100_set_dispregs(struct w100fb_par*);
47static void w100_update_enable(void);
48static void w100_update_disable(void);
49static void calc_hsync(struct w100fb_par *par);
50struct w100_pll_info *w100_get_xtal_table(unsigned int freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52/* Pseudo palette size */
53#define MAX_PALETTES 16
54
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define W100_SUSPEND_EXTMEM 0
56#define W100_SUSPEND_ALL 1
57
Richard Purdieaac51f02005-09-06 15:19:03 -070058#define BITS_PER_PIXEL 16
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
61static void *remapped_base;
62static void *remapped_regs;
63static void *remapped_fbuf;
64
Richard Purdieaac51f02005-09-06 15:19:03 -070065#define REMAPPED_FB_LEN 0x15ffff
66
67/* This is the offset in the w100's address space we map the current
68 framebuffer memory to. We use the position of external memory as
69 we can remap internal memory to there if external isn't present. */
70#define W100_FB_BASE MEM_EXT_BASE_VALUE
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73/*
74 * Sysfs functions
75 */
Richard Purdieaac51f02005-09-06 15:19:03 -070076static ssize_t flip_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 struct fb_info *info = dev_get_drvdata(dev);
79 struct w100fb_par *par=info->par;
80
Richard Purdieaac51f02005-09-06 15:19:03 -070081 return sprintf(buf, "%d\n",par->flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082}
83
Richard Purdieaac51f02005-09-06 15:19:03 -070084static ssize_t flip_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
Richard Purdieaac51f02005-09-06 15:19:03 -070086 unsigned int flip;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 struct fb_info *info = dev_get_drvdata(dev);
88 struct w100fb_par *par=info->par;
89
Richard Purdieaac51f02005-09-06 15:19:03 -070090 flip = simple_strtoul(buf, NULL, 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Richard Purdieaac51f02005-09-06 15:19:03 -070092 if (flip > 0)
93 par->flip = 1;
94 else
95 par->flip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
Richard Purdieaac51f02005-09-06 15:19:03 -070097 w100_update_disable();
98 w100_set_dispregs(par);
99 w100_update_enable();
100
101 calc_hsync(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
103 return count;
104}
105
Richard Purdieaac51f02005-09-06 15:19:03 -0700106static DEVICE_ATTR(flip, 0644, flip_show, flip_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Yani Ioannou060b8842005-05-17 06:44:04 -0400108static ssize_t w100fb_reg_read(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109{
Richard Purdieaac51f02005-09-06 15:19:03 -0700110 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 regs = simple_strtoul(buf, NULL, 16);
112 param = readl(remapped_regs + regs);
113 printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
114 return count;
115}
116
117static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
118
Yani Ioannou060b8842005-05-17 06:44:04 -0400119static ssize_t w100fb_reg_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
Richard Purdieaac51f02005-09-06 15:19:03 -0700121 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 sscanf(buf, "%lx %lx", &regs, &param);
123
124 if (regs <= 0x2000) {
125 printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
126 writel(param, remapped_regs + regs);
127 }
128
129 return count;
130}
131
132static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
133
134
Richard Purdieaac51f02005-09-06 15:19:03 -0700135static ssize_t fastpllclk_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
137 struct fb_info *info = dev_get_drvdata(dev);
138 struct w100fb_par *par=info->par;
139
Richard Purdieaac51f02005-09-06 15:19:03 -0700140 return sprintf(buf, "%d\n",par->fastpll_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
Richard Purdieaac51f02005-09-06 15:19:03 -0700143static ssize_t fastpllclk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 struct fb_info *info = dev_get_drvdata(dev);
146 struct w100fb_par *par=info->par;
147
Richard Purdieaac51f02005-09-06 15:19:03 -0700148 if (simple_strtoul(buf, NULL, 10) > 0) {
149 par->fastpll_mode=1;
150 printk("w100fb: Using fast system clock (if possible)\n");
151 } else {
152 par->fastpll_mode=0;
153 printk("w100fb: Using normal system clock\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700155
156 w100_init_clocks(par);
157 calc_hsync(par);
158
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 return count;
160}
161
Richard Purdieaac51f02005-09-06 15:19:03 -0700162static DEVICE_ATTR(fastpllclk, 0644, fastpllclk_show, fastpllclk_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700165 * Some touchscreens need hsync information from the video driver to
166 * function correctly. We export it here.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700168unsigned long w100fb_get_hsynclen(struct device *dev)
169{
170 struct fb_info *info = dev_get_drvdata(dev);
171 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172
Richard Purdieaac51f02005-09-06 15:19:03 -0700173 /* If display is blanked/suspended, hsync isn't active */
174 if (par->blanked)
175 return 0;
176 else
177 return par->hsync_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}
Richard Purdieaac51f02005-09-06 15:19:03 -0700179EXPORT_SYMBOL(w100fb_get_hsynclen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
Richard Purdieaac51f02005-09-06 15:19:03 -0700181static void w100fb_clear_screen(struct w100fb_par *par)
182{
183 memset_io(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), 0, (par->xres * par->yres * BITS_PER_PIXEL/8));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
186
187/*
188 * Set a palette value from rgb components
189 */
190static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
191 u_int trans, struct fb_info *info)
192{
193 unsigned int val;
194 int ret = 1;
195
196 /*
197 * If greyscale is true, then we convert the RGB value
198 * to greyscale no matter what visual we are using.
199 */
200 if (info->var.grayscale)
201 red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
202
203 /*
204 * 16-bit True Colour. We encode the RGB value
205 * according to the RGB bitfield information.
206 */
207 if (regno < MAX_PALETTES) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 u32 *pal = info->pseudo_palette;
209
210 val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
211 pal[regno] = val;
212 ret = 0;
213 }
214 return ret;
215}
216
217
218/*
219 * Blank the display based on value in blank_mode
220 */
221static int w100fb_blank(int blank_mode, struct fb_info *info)
222{
Richard Purdieaac51f02005-09-06 15:19:03 -0700223 struct w100fb_par *par = info->par;
224 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
226 switch(blank_mode) {
227
Richard Purdieaac51f02005-09-06 15:19:03 -0700228 case FB_BLANK_NORMAL: /* Normal blanking */
229 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
230 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
231 case FB_BLANK_POWERDOWN: /* Poweroff */
232 if (par->blanked == 0) {
233 if(tg && tg->suspend)
234 tg->suspend(par);
235 par->blanked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 }
237 break;
238
239 case FB_BLANK_UNBLANK: /* Unblanking */
Richard Purdieaac51f02005-09-06 15:19:03 -0700240 if (par->blanked != 0) {
241 if(tg && tg->resume)
242 tg->resume(par);
243 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245 break;
246 }
247 return 0;
248}
249
Richard Purdieaac51f02005-09-06 15:19:03 -0700250
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251/*
252 * Change the resolution by calling the appropriate hardware functions
253 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700254static void w100fb_activate_var(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
Richard Purdieaac51f02005-09-06 15:19:03 -0700256 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Richard Purdieaac51f02005-09-06 15:19:03 -0700258 w100_pwm_setup(par);
259 w100_setup_memory(par);
260 w100_init_clocks(par);
261 w100fb_clear_screen(par);
262 w100_vsync();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
Richard Purdieaac51f02005-09-06 15:19:03 -0700264 w100_update_disable();
265 w100_init_lcd(par);
266 w100_set_dispregs(par);
267 w100_update_enable();
268
269 calc_hsync(par);
270
271 if (!par->blanked && tg && tg->change)
272 tg->change(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273}
274
Richard Purdieaac51f02005-09-06 15:19:03 -0700275
276/* Select the smallest mode that allows the desired resolution to be
277 * displayed. If desired, the x and y parameters can be rounded up to
278 * match the selected mode.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700280static struct w100_mode *w100fb_get_mode(struct w100fb_par *par, unsigned int *x, unsigned int *y, int saveval)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
Richard Purdieaac51f02005-09-06 15:19:03 -0700282 struct w100_mode *mode = NULL;
283 struct w100_mode *modelist = par->mach->modelist;
284 unsigned int best_x = 0xffffffff, best_y = 0xffffffff;
285 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Richard Purdieaac51f02005-09-06 15:19:03 -0700287 for (i = 0 ; i < par->mach->num_modes ; i++) {
288 if (modelist[i].xres >= *x && modelist[i].yres >= *y &&
289 modelist[i].xres < best_x && modelist[i].yres < best_y) {
290 best_x = modelist[i].xres;
291 best_y = modelist[i].yres;
292 mode = &modelist[i];
293 } else if(modelist[i].xres >= *y && modelist[i].yres >= *x &&
294 modelist[i].xres < best_y && modelist[i].yres < best_x) {
295 best_x = modelist[i].yres;
296 best_y = modelist[i].xres;
297 mode = &modelist[i];
298 }
299 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Richard Purdieaac51f02005-09-06 15:19:03 -0700301 if (mode && saveval) {
302 *x = best_x;
303 *y = best_y;
304 }
305
306 return mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309
310/*
311 * w100fb_check_var():
312 * Get the video params out of 'var'. If a value doesn't fit, round it up,
313 * if it's too big, return -EINVAL.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 */
315static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
316{
Richard Purdieaac51f02005-09-06 15:19:03 -0700317 struct w100fb_par *par=info->par;
318
319 if(!w100fb_get_mode(par, &var->xres, &var->yres, 1))
320 return -EINVAL;
321
322 if (par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (par->mach->mem->size+1)))
323 return -EINVAL;
324
325 if (!par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)))
326 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 var->xres_virtual = max(var->xres_virtual, var->xres);
329 var->yres_virtual = max(var->yres_virtual, var->yres);
330
331 if (var->bits_per_pixel > BITS_PER_PIXEL)
332 return -EINVAL;
333 else
334 var->bits_per_pixel = BITS_PER_PIXEL;
335
336 var->red.offset = 11;
337 var->red.length = 5;
338 var->green.offset = 5;
339 var->green.length = 6;
340 var->blue.offset = 0;
341 var->blue.length = 5;
342 var->transp.offset = var->transp.length = 0;
343
344 var->nonstd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 var->height = -1;
346 var->width = -1;
347 var->vmode = FB_VMODE_NONINTERLACED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 var->sync = 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700349 var->pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 return 0;
352}
353
354
355/*
356 * w100fb_set_par():
357 * Set the user defined part of the display for the specified console
358 * by looking at the values in info.var
359 */
360static int w100fb_set_par(struct fb_info *info)
361{
362 struct w100fb_par *par=info->par;
363
Richard Purdieaac51f02005-09-06 15:19:03 -0700364 if (par->xres != info->var.xres || par->yres != info->var.yres) {
365 par->xres = info->var.xres;
366 par->yres = info->var.yres;
367 par->mode = w100fb_get_mode(par, &par->xres, &par->yres, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Richard Purdieaac51f02005-09-06 15:19:03 -0700369 info->fix.visual = FB_VISUAL_TRUECOLOR;
370 info->fix.ypanstep = 0;
371 info->fix.ywrapstep = 0;
372 info->fix.line_length = par->xres * BITS_PER_PIXEL / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
Richard Purdieaac51f02005-09-06 15:19:03 -0700374 if ((par->xres*par->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)) {
375 par->extmem_active = 1;
376 info->fix.smem_len = par->mach->mem->size+1;
377 } else {
378 par->extmem_active = 0;
379 info->fix.smem_len = MEM_INT_SIZE+1;
380 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Richard Purdieaac51f02005-09-06 15:19:03 -0700382 w100fb_activate_var(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 return 0;
385}
386
387
388/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700389 * Frame buffer operations
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 */
391static struct fb_ops w100fb_ops = {
Richard Purdieaac51f02005-09-06 15:19:03 -0700392 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 .fb_check_var = w100fb_check_var,
Richard Purdieaac51f02005-09-06 15:19:03 -0700394 .fb_set_par = w100fb_set_par,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 .fb_setcolreg = w100fb_setcolreg,
Richard Purdieaac51f02005-09-06 15:19:03 -0700396 .fb_blank = w100fb_blank,
397 .fb_fillrect = cfb_fillrect,
398 .fb_copyarea = cfb_copyarea,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 .fb_imageblit = cfb_imageblit,
Richard Purdieaac51f02005-09-06 15:19:03 -0700400 .fb_cursor = soft_cursor,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401};
402
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403#ifdef CONFIG_PM
Richard Purdieaac51f02005-09-06 15:19:03 -0700404static void w100fb_save_vidmem(struct w100fb_par *par)
405{
406 int memsize;
407
408 if (par->extmem_active) {
409 memsize=par->mach->mem->size;
410 par->saved_extmem = vmalloc(memsize);
411 if (par->saved_extmem)
412 memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
413 }
414 memsize=MEM_INT_SIZE;
415 par->saved_intmem = vmalloc(memsize);
416 if (par->saved_intmem && par->extmem_active)
417 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
418 else if (par->saved_intmem)
419 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
420}
421
422static void w100fb_restore_vidmem(struct w100fb_par *par)
423{
424 int memsize;
425
426 if (par->extmem_active && par->saved_extmem) {
427 memsize=par->mach->mem->size;
428 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
429 vfree(par->saved_extmem);
430 }
431 if (par->saved_intmem) {
432 memsize=MEM_INT_SIZE;
433 if (par->extmem_active)
434 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
435 else
436 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
437 vfree(par->saved_intmem);
438 }
439}
440
441static int w100fb_suspend(struct device *dev, pm_message_t state, uint32_t level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
443 if (level == SUSPEND_POWER_DOWN) {
444 struct fb_info *info = dev_get_drvdata(dev);
445 struct w100fb_par *par=info->par;
Richard Purdieaac51f02005-09-06 15:19:03 -0700446 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Richard Purdieaac51f02005-09-06 15:19:03 -0700448 w100fb_save_vidmem(par);
449 if(tg && tg->suspend)
450 tg->suspend(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 w100_suspend(W100_SUSPEND_ALL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700452 par->blanked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 }
454 return 0;
455}
456
Richard Purdieaac51f02005-09-06 15:19:03 -0700457static int w100fb_resume(struct device *dev, uint32_t level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458{
459 if (level == RESUME_POWER_ON) {
460 struct fb_info *info = dev_get_drvdata(dev);
461 struct w100fb_par *par=info->par;
Richard Purdieaac51f02005-09-06 15:19:03 -0700462 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Richard Purdieaac51f02005-09-06 15:19:03 -0700464 w100_hw_init(par);
465 w100fb_activate_var(par);
466 w100fb_restore_vidmem(par);
467 if(tg && tg->resume)
468 tg->resume(par);
469 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 }
471 return 0;
472}
473#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700474#define w100fb_suspend NULL
475#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476#endif
477
478
479int __init w100fb_probe(struct device *dev)
480{
Richard Purdieaac51f02005-09-06 15:19:03 -0700481 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700483 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 struct w100fb_par *par;
485 struct platform_device *pdev = to_platform_device(dev);
486 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700487 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
489 if (!mem)
490 return -EINVAL;
491
Richard Purdieaac51f02005-09-06 15:19:03 -0700492 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
494 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700495 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Richard Purdieaac51f02005-09-06 15:19:03 -0700497 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700499 if (remapped_regs == NULL)
500 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Richard Purdieaac51f02005-09-06 15:19:03 -0700502 /* Identify the chip */
503 printk("Found ");
504 chip_id = readl(remapped_regs + mmCHIP_ID);
505 switch(chip_id) {
506 case CHIP_ID_W100: printk("w100"); break;
507 case CHIP_ID_W3200: printk("w3200"); break;
508 case CHIP_ID_W3220: printk("w3220"); break;
509 default:
510 printk("Unknown imageon chip ID\n");
511 err = -ENODEV;
512 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700514 printk(" at 0x%08lx.\n", mem->start+W100_CFG_BASE);
515
516 /* Remap the framebuffer */
517 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
518 if (remapped_fbuf == NULL)
519 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
521 info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
522 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700523 err = -ENOMEM;
524 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 dev_set_drvdata(dev, info);
529
530 inf = dev->platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700531 par->chip_id = chip_id;
532 par->mach = inf;
533 par->fastpll_mode = 0;
534 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Richard Purdieaac51f02005-09-06 15:19:03 -0700536 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
537 if (!par->pll_table) {
538 printk(KERN_ERR "No matching Xtal definition found\n");
539 err = -EINVAL;
540 goto out;
541 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
543 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
544 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700545 err = -ENOMEM;
546 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
548
549 info->fbops = &w100fb_ops;
550 info->flags = FBINFO_DEFAULT;
551 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700552 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 info->screen_size = REMAPPED_FB_LEN;
554
Richard Purdieaac51f02005-09-06 15:19:03 -0700555 strcpy(info->fix.id, "w100fb");
556 info->fix.type = FB_TYPE_PACKED_PIXELS;
557 info->fix.type_aux = 0;
558 info->fix.accel = FB_ACCEL_NONE;
559 info->fix.smem_start = mem->start+W100_FB_BASE;
560 info->fix.mmio_start = mem->start+W100_REG_BASE;
561 info->fix.mmio_len = W100_REG_LEN;
562
563 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
564 err = -ENOMEM;
565 goto out;
566 }
567
568 par->mode = &inf->modelist[0];
569 if(inf->init_mode & INIT_MODE_ROTATED) {
570 info->var.xres = par->mode->yres;
571 info->var.yres = par->mode->xres;
572 }
573 else {
574 info->var.xres = par->mode->xres;
575 info->var.yres = par->mode->yres;
576 }
577
578 if(inf->init_mode &= INIT_MODE_FLIPPED)
579 par->flip = 1;
580 else
581 par->flip = 0;
582
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700585 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 info->var.sync = 0;
587 info->var.grayscale = 0;
588 info->var.xoffset = info->var.yoffset = 0;
589 info->var.accel_flags = 0;
590 info->var.activate = FB_ACTIVATE_NOW;
591
Richard Purdieaac51f02005-09-06 15:19:03 -0700592 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Richard Purdieaac51f02005-09-06 15:19:03 -0700594 if (w100fb_check_var(&info->var, info) < 0) {
595 err = -EINVAL;
596 goto out;
597 }
598
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 w100fb_set_par(info);
600
601 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700602 err = -EINVAL;
603 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
605
Richard Purdieaac51f02005-09-06 15:19:03 -0700606 device_create_file(dev, &dev_attr_fastpllclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 device_create_file(dev, &dev_attr_reg_read);
608 device_create_file(dev, &dev_attr_reg_write);
Richard Purdieaac51f02005-09-06 15:19:03 -0700609 device_create_file(dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
612 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700613out:
614 fb_dealloc_cmap(&info->cmap);
615 kfree(info->pseudo_palette);
616 if (remapped_fbuf != NULL)
617 iounmap(remapped_fbuf);
618 if (remapped_regs != NULL)
619 iounmap(remapped_regs);
620 if (remapped_base != NULL)
621 iounmap(remapped_base);
622 if (info)
623 framebuffer_release(info);
624 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
627
628static int w100fb_remove(struct device *dev)
629{
630 struct fb_info *info = dev_get_drvdata(dev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700631 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Richard Purdieaac51f02005-09-06 15:19:03 -0700633 device_remove_file(dev, &dev_attr_fastpllclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 device_remove_file(dev, &dev_attr_reg_read);
635 device_remove_file(dev, &dev_attr_reg_write);
Richard Purdieaac51f02005-09-06 15:19:03 -0700636 device_remove_file(dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 unregister_framebuffer(info);
639
Richard Purdieaac51f02005-09-06 15:19:03 -0700640 vfree(par->saved_intmem);
641 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700643 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 iounmap(remapped_base);
646 iounmap(remapped_regs);
647 iounmap(remapped_fbuf);
648
649 framebuffer_release(info);
650
651 return 0;
652}
653
654
655/* ------------------- chipset specific functions -------------------------- */
656
657
658static void w100_soft_reset(void)
659{
660 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
661 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
662 udelay(100);
663 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
664 udelay(100);
665}
666
Richard Purdieaac51f02005-09-06 15:19:03 -0700667static void w100_update_disable(void)
668{
669 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
670
671 /* Prevent display updates */
672 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
673 disp_db_buf_wr_cntl.f.update_db_buf = 0;
674 disp_db_buf_wr_cntl.f.en_db_buf = 0;
675 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
676}
677
678static void w100_update_enable(void)
679{
680 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
681
682 /* Enable display updates */
683 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
684 disp_db_buf_wr_cntl.f.update_db_buf = 1;
685 disp_db_buf_wr_cntl.f.en_db_buf = 1;
686 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
687}
688
689unsigned long w100fb_gpio_read(int port)
690{
691 unsigned long value;
692
693 if (port==W100_GPIO_PORT_A)
694 value = readl(remapped_regs + mmGPIO_DATA);
695 else
696 value = readl(remapped_regs + mmGPIO_DATA2);
697
698 return value;
699}
700
701void w100fb_gpio_write(int port, unsigned long value)
702{
703 if (port==W100_GPIO_PORT_A)
704 value = writel(value, remapped_regs + mmGPIO_DATA);
705 else
706 value = writel(value, remapped_regs + mmGPIO_DATA2);
707}
708EXPORT_SYMBOL(w100fb_gpio_read);
709EXPORT_SYMBOL(w100fb_gpio_write);
710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711/*
712 * Initialization of critical w100 hardware
713 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700714static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
716 u32 temp32;
717 union cif_cntl_u cif_cntl;
718 union intf_cntl_u intf_cntl;
719 union cfgreg_base_u cfgreg_base;
720 union wrap_top_dir_u wrap_top_dir;
721 union cif_read_dbg_u cif_read_dbg;
722 union cpu_defaults_u cpu_default;
723 union cif_write_dbg_u cif_write_dbg;
724 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700726 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 w100_soft_reset();
729
730 /* This is what the fpga_init code does on reset. May be wrong
731 but there is little info available */
732 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
733 for (temp32 = 0; temp32 < 10000; temp32++)
734 readl(remapped_regs + mmSCRATCH_UMSK);
735 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
736
737 /* Set up CIF */
738 cif_io.val = defCIF_IO;
739 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
740
741 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
742 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
743 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
744 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
745 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
746
747 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
748 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
749 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
750
751 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
752 cif_cntl.f.dis_system_bits = 1;
753 cif_cntl.f.dis_mr = 1;
754 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
755 cif_cntl.f.intb_oe = 1;
756 cif_cntl.f.interrupt_active_high = 1;
757 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
758
759 /* Setup cfgINTF_CNTL and cfgCPU defaults */
760 intf_cntl.val = defINTF_CNTL;
761 intf_cntl.f.ad_inc_a = 1;
762 intf_cntl.f.ad_inc_b = 1;
763 intf_cntl.f.rd_data_rdy_a = 0;
764 intf_cntl.f.rd_data_rdy_b = 0;
765 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
766
767 cpu_default.val = defCPU_DEFAULTS;
768 cpu_default.f.access_ind_addr_a = 1;
769 cpu_default.f.access_ind_addr_b = 1;
770 cpu_default.f.access_scratch_reg = 1;
771 cpu_default.f.transition_size = 0;
772 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
773
774 /* set up the apertures */
775 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
776
777 cfgreg_base.val = defCFGREG_BASE;
778 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
779 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 wrap_start_dir.val = defWRAP_START_DIR;
782 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
783 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
784
785 wrap_top_dir.val = defWRAP_TOP_DIR;
786 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
787 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
788
789 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700790
791 /* Set the hardware to 565 colour */
792 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
793 temp32 &= 0xff7fffff;
794 temp32 |= 0x00800000;
795 writel(temp32, remapped_regs + mmDISP_DEBUG2);
796
797 /* Initialise the GPIO lines */
798 if (gpio) {
799 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
800 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
801 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
802 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
803 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
804 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
805 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806}
807
808
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809struct power_state {
810 union clk_pin_cntl_u clk_pin_cntl;
811 union pll_ref_fb_div_u pll_ref_fb_div;
812 union pll_cntl_u pll_cntl;
813 union sclk_cntl_u sclk_cntl;
814 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700816 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817};
818
819
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820static struct power_state w100_pwr_state;
821
Richard Purdieaac51f02005-09-06 15:19:03 -0700822/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
823
824/* 12.5MHz Crystal PLL Table */
825static struct w100_pll_info xtal_12500000[] = {
826 /*freq M N_int N_fac tfgoal lock_time */
827 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
828 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
829 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
830 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
831 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
832 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833};
834
Richard Purdieaac51f02005-09-06 15:19:03 -0700835/* 14.318MHz Crystal PLL Table */
836static struct w100_pll_info xtal_14318000[] = {
837 /*freq M N_int N_fac tfgoal lock_time */
838 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
839 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
840 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
841 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
842 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
843 { 0, 0, 0, 0, 0, 0},
844};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
Richard Purdieaac51f02005-09-06 15:19:03 -0700846/* 16MHz Crystal PLL Table */
847static struct w100_pll_info xtal_16000000[] = {
848 /*freq M N_int N_fac tfgoal lock_time */
849 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
850 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
851 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
852 { 0, 0, 0, 0, 0, 0},
853};
854
855static struct pll_entries {
856 int xtal_freq;
857 struct w100_pll_info *pll_table;
858} w100_pll_tables[] = {
859 { 12500000, &xtal_12500000[0] },
860 { 14318000, &xtal_14318000[0] },
861 { 16000000, &xtal_16000000[0] },
862 { 0 },
863};
864
865struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866{
Richard Purdieaac51f02005-09-06 15:19:03 -0700867 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Richard Purdieaac51f02005-09-06 15:19:03 -0700869 do {
870 if (freq == pll_entry->xtal_freq)
871 return pll_entry->pll_table;
872 pll_entry++;
873 } while (pll_entry->xtal_freq);
874 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875}
876
877
Richard Purdieaac51f02005-09-06 15:19:03 -0700878static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879{
Richard Purdieaac51f02005-09-06 15:19:03 -0700880 union clk_test_cntl_u clk_test_cntl;
881
882 udelay(5);
883
884 /* Select the test clock source and reset */
885 clk_test_cntl.f.start_check_freq = 0x0;
886 clk_test_cntl.f.testclk_sel = testclk_sel;
887 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
888 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
889
890 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
891 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
892
893 /* Run clock test */
894 clk_test_cntl.f.start_check_freq = 0x1;
895 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
896
897 /* Give the test time to complete */
898 udelay(20);
899
900 /* Return the result */
901 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
902 clk_test_cntl.f.start_check_freq = 0x0;
903 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
904
905 return clk_test_cntl.f.test_count;
906}
907
908
909static int w100_pll_adjust(struct w100_pll_info *pll)
910{
911 unsigned int tf80;
912 unsigned int tf20;
913
914 /* Initial Settings */
915 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
916 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
917 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
918 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
919 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
920 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
921 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
922
923 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
924 * therefore, commented out the following lines
925 * tf80 meant tf100
926 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 do {
Richard Purdieaac51f02005-09-06 15:19:03 -0700928 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
930 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
931
Richard Purdieaac51f02005-09-06 15:19:03 -0700932 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
933 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 /* set VCO input = 0.2 * VDD */
935 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
936 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
937
Richard Purdieaac51f02005-09-06 15:19:03 -0700938 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
939 if (tf20 <= (pll->tfgoal))
940 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -0700943 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
944 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 /* slow VCO config */
946 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
947 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
948 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 continue;
950 }
951 }
952 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
953 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700954 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
956 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700957 } else {
958 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 } while(1);
961}
962
963
964/*
965 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700967static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968{
Richard Purdieaac51f02005-09-06 15:19:03 -0700969 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
Richard Purdieaac51f02005-09-06 15:19:03 -0700971 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 /* set VCO input = 0.5 * VDD */
975 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
976 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
977
Richard Purdieaac51f02005-09-06 15:19:03 -0700978 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
980 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -0700981 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
983
Richard Purdieaac51f02005-09-06 15:19:03 -0700984 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
986 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
987
Richard Purdieaac51f02005-09-06 15:19:03 -0700988 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989
990 /* PLL locked */
991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 return status;
993}
994
995
Richard Purdieaac51f02005-09-06 15:19:03 -0700996static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Richard Purdieaac51f02005-09-06 15:19:03 -0700998 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Richard Purdieaac51f02005-09-06 15:19:03 -07001000 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001002 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
1003 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1005 }
1006
Richard Purdieaac51f02005-09-06 15:19:03 -07001007 /* Set system clock source to XTAL whilst adjusting the PLL! */
1008 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1010
Richard Purdieaac51f02005-09-06 15:19:03 -07001011 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1012 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1013 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1014 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1016
1017 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1018 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1019
Richard Purdieaac51f02005-09-06 15:19:03 -07001020 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
Richard Purdieaac51f02005-09-06 15:19:03 -07001022 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001024 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1025 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1027 }
1028 return status;
1029}
1030
Richard Purdieaac51f02005-09-06 15:19:03 -07001031/* freq = target frequency of the PLL */
1032static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
Richard Purdieaac51f02005-09-06 15:19:03 -07001034 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
Richard Purdieaac51f02005-09-06 15:19:03 -07001036 do {
1037 if (freq == pll->freq) {
1038 return w100_pll_set_clk(pll);
1039 }
1040 pll++;
1041 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 return 0;
1043}
1044
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045/* Set up an initial state. Some values/fields set
1046 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001047static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048{
1049 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1050 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1051 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1052 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001053 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1055 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1056
Richard Purdieaac51f02005-09-06 15:19:03 -07001057 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1058 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001060 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001062 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1063 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1064 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1065 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1066 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1067 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1068 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1069 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1070 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1072 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1073 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1074 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1075 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1076
Richard Purdieaac51f02005-09-06 15:19:03 -07001077 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1078 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1079 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1081
Richard Purdieaac51f02005-09-06 15:19:03 -07001082 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1083 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1085 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1086 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1087 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1088
1089 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1090 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1091 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001092 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1094 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1095 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1096 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1097 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1098 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1099 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1100 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1101 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001102 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1104 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1105 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1106 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1107 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1108
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001110 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1112 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1113 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001114 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1115 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1117 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1118 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1119
Richard Purdieaac51f02005-09-06 15:19:03 -07001120 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121}
1122
1123
Richard Purdieaac51f02005-09-06 15:19:03 -07001124/*
1125 * Setup the w100 clocks for the specified mode
1126 */
1127static void w100_init_clocks(struct w100fb_par *par)
1128{
1129 struct w100_mode *mode = par->mode;
1130
1131 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1132 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1133
1134 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1135 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1136 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1137 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1138}
1139
1140static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141{
1142 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001143 struct w100_mode *mode = par->mode;
1144 struct w100_gen_regs *regs = par->mach->regs;
1145 union active_h_disp_u active_h_disp;
1146 union active_v_disp_u active_v_disp;
1147 union graphic_h_disp_u graphic_h_disp;
1148 union graphic_v_disp_u graphic_v_disp;
1149 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
Richard Purdieaac51f02005-09-06 15:19:03 -07001151 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152
Richard Purdieaac51f02005-09-06 15:19:03 -07001153 active_h_disp.val = 0;
1154 active_h_disp.f.active_h_start=mode->left_margin;
1155 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1156 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Richard Purdieaac51f02005-09-06 15:19:03 -07001158 active_v_disp.val = 0;
1159 active_v_disp.f.active_v_start=mode->upper_margin;
1160 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1161 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
Richard Purdieaac51f02005-09-06 15:19:03 -07001163 graphic_h_disp.val = 0;
1164 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1165 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1166 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1167
1168 graphic_v_disp.val = 0;
1169 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1170 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1171 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1172
1173 crtc_total.val = 0;
1174 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1175 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1176 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1177
1178 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1179 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1180 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1181 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1182 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1183 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1184 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1185 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1186 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1187
1188 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1189 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1190 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1191 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1192 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1193 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1194
1195 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1196 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1197 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1198 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 /* Hack for overlay in ext memory */
1201 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1202 temp32 |= 0xc0000000;
1203 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204}
1205
1206
Richard Purdieaac51f02005-09-06 15:19:03 -07001207static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208{
Richard Purdieaac51f02005-09-06 15:19:03 -07001209 union mc_ext_mem_location_u extmem_location;
1210 union mc_fb_location_u intmem_location;
1211 struct w100_mem_info *mem = par->mach->mem;
1212 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1213
1214 if (!par->extmem_active) {
1215 w100_suspend(W100_SUSPEND_EXTMEM);
1216
1217 /* Map Internal Memory at FB Base */
1218 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1219 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1220 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1221
1222 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1223 to acceleration libraries */
1224 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1225 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1226 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1227 } else {
1228 /* Map Internal Memory to its default location */
1229 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1230 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1231 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1232
1233 /* Map External Memory at FB Base */
1234 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1235 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1236 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1237
1238 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1239 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1240 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1241 udelay(100);
1242 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1243 udelay(100);
1244 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1245 udelay(100);
1246 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1247 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1248 if (bm_mem) {
1249 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1250 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1251 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1252 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1253 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1254 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1255 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1256 }
1257 }
1258}
1259
1260static void w100_set_dispregs(struct w100fb_par *par)
1261{
1262 unsigned long rot=0, divider, offset=0;
1263 union graphic_ctrl_u graphic_ctrl;
1264
1265 /* See if the mode has been rotated */
1266 if (par->xres == par->mode->xres) {
1267 if (par->flip) {
1268 rot=3; /* 180 degree */
1269 offset=(par->xres * par->yres) - 1;
1270 } /* else 0 degree */
1271 divider = par->mode->pixclk_divider;
1272 } else {
1273 if (par->flip) {
1274 rot=2; /* 270 degree */
1275 offset=par->xres - 1;
1276 } else {
1277 rot=1; /* 90 degree */
1278 offset=par->xres * (par->yres - 1);
1279 }
1280 divider = par->mode->pixclk_divider_rotated;
1281 }
1282
1283 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1284 switch (par->chip_id) {
1285 case CHIP_ID_W100:
1286 graphic_ctrl.f_w100.color_depth=6;
1287 graphic_ctrl.f_w100.en_crtc=1;
1288 graphic_ctrl.f_w100.en_graphic_req=1;
1289 graphic_ctrl.f_w100.en_graphic_crtc=1;
1290 graphic_ctrl.f_w100.lcd_pclk_on=1;
1291 graphic_ctrl.f_w100.lcd_sclk_on=1;
1292 graphic_ctrl.f_w100.low_power_on=0;
1293 graphic_ctrl.f_w100.req_freq=0;
1294 graphic_ctrl.f_w100.portrait_mode=rot;
1295
1296 /* Zaurus needs this */
1297 switch(par->xres) {
1298 case 240:
1299 case 320:
1300 default:
1301 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1302 break;
1303 case 480:
1304 case 640:
1305 switch(rot) {
1306 case 0: /* 0 */
1307 case 3: /* 180 */
1308 graphic_ctrl.f_w100.low_power_on=1;
1309 graphic_ctrl.f_w100.req_freq=5;
1310 break;
1311 case 1: /* 90 */
1312 case 2: /* 270 */
1313 graphic_ctrl.f_w100.req_freq=4;
1314 break;
1315 default:
1316 break;
1317 }
1318 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1319 break;
1320 }
1321 break;
1322 case CHIP_ID_W3200:
1323 case CHIP_ID_W3220:
1324 graphic_ctrl.f_w32xx.color_depth=6;
1325 graphic_ctrl.f_w32xx.en_crtc=1;
1326 graphic_ctrl.f_w32xx.en_graphic_req=1;
1327 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1328 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1329 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1330 graphic_ctrl.f_w32xx.low_power_on=0;
1331 graphic_ctrl.f_w32xx.req_freq=0;
1332 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1333 graphic_ctrl.f_w32xx.portrait_mode=rot;
1334 break;
1335 }
1336
1337 /* Set the pixel clock source and divider */
1338 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1340 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1341
Richard Purdieaac51f02005-09-06 15:19:03 -07001342 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1343 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1344 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345}
1346
1347
Richard Purdieaac51f02005-09-06 15:19:03 -07001348/*
1349 * Work out how long the sync pulse lasts
1350 * Value is 1/(time in seconds)
1351 */
1352static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353{
Richard Purdieaac51f02005-09-06 15:19:03 -07001354 unsigned long hsync;
1355 struct w100_mode *mode = par->mode;
1356 union crtc_ss_u crtc_ss;
1357
1358 if (mode->pixclk_src == CLK_SRC_XTAL)
1359 hsync=par->mach->xtal_freq;
1360 else
1361 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1362
1363 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1364
1365 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1366 if (crtc_ss.val)
1367 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1368 else
1369 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370}
1371
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372static void w100_suspend(u32 mode)
1373{
1374 u32 val;
1375
1376 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1377 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1378
1379 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001380 val &= ~(0x00100000); /* bit20=0 */
1381 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1383
1384 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001385 val &= ~(0x00040000); /* bit18=0 */
1386 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1388
Richard Purdieaac51f02005-09-06 15:19:03 -07001389 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
1391 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 /* CKE: Tri-State */
1393 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001394 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1396
1397 /* CLK: Stop */
1398 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001399 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1401 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1403 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1404 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1405
1406 udelay(5);
1407
1408 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001409 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 writel(val, remapped_regs + mmPLL_CNTL);
1411 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1412 }
1413}
1414
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415static void w100_vsync(void)
1416{
1417 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001418 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
1420 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1421
1422 /* set vline pos */
1423 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1424
1425 /* disable vline irq */
1426 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1427
1428 tmp &= ~0x00000002;
1429 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1430
1431 /* clear vline irq status */
1432 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1433
1434 /* enable vline irq */
1435 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1436
1437 /* clear vline irq status */
1438 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1439
1440 while(timeout > 0) {
1441 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1442 break;
1443 udelay(1);
1444 timeout--;
1445 }
1446
1447 /* disable vline irq */
1448 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1449
1450 /* clear vline irq status */
1451 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1452}
1453
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454static struct device_driver w100fb_driver = {
1455 .name = "w100fb",
1456 .bus = &platform_bus_type,
1457 .probe = w100fb_probe,
1458 .remove = w100fb_remove,
1459 .suspend = w100fb_suspend,
1460 .resume = w100fb_resume,
1461};
1462
1463int __devinit w100fb_init(void)
1464{
1465 return driver_register(&w100fb_driver);
1466}
1467
1468void __exit w100fb_cleanup(void)
1469{
1470 driver_unregister(&w100fb_driver);
1471}
1472
1473module_init(w100fb_init);
1474module_exit(w100fb_cleanup);
1475
1476MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001477MODULE_LICENSE("GPL");