blob: 752bf88906a9de13b6578c8c0f2d989af87e795a [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
Russell King9480e302005-10-28 09:52:56 -0700441static int w100fb_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
Russell King9480e302005-10-28 09:52:56 -0700443 struct fb_info *info = dev_get_drvdata(dev);
444 struct w100fb_par *par=info->par;
445 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446
Russell King9480e302005-10-28 09:52:56 -0700447 w100fb_save_vidmem(par);
448 if(tg && tg->suspend)
449 tg->suspend(par);
450 w100_suspend(W100_SUSPEND_ALL);
451 par->blanked = 1;
452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 return 0;
454}
455
Russell King9480e302005-10-28 09:52:56 -0700456static int w100fb_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Russell King9480e302005-10-28 09:52:56 -0700458 struct fb_info *info = dev_get_drvdata(dev);
459 struct w100fb_par *par=info->par;
460 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Russell King9480e302005-10-28 09:52:56 -0700462 w100_hw_init(par);
463 w100fb_activate_var(par);
464 w100fb_restore_vidmem(par);
465 if(tg && tg->resume)
466 tg->resume(par);
467 par->blanked = 0;
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 return 0;
470}
471#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700472#define w100fb_suspend NULL
473#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474#endif
475
476
477int __init w100fb_probe(struct device *dev)
478{
Richard Purdieaac51f02005-09-06 15:19:03 -0700479 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700481 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 struct w100fb_par *par;
483 struct platform_device *pdev = to_platform_device(dev);
484 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700485 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 if (!mem)
488 return -EINVAL;
489
Richard Purdieaac51f02005-09-06 15:19:03 -0700490 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
492 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700493 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
Richard Purdieaac51f02005-09-06 15:19:03 -0700495 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700497 if (remapped_regs == NULL)
498 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Richard Purdieaac51f02005-09-06 15:19:03 -0700500 /* Identify the chip */
501 printk("Found ");
502 chip_id = readl(remapped_regs + mmCHIP_ID);
503 switch(chip_id) {
504 case CHIP_ID_W100: printk("w100"); break;
505 case CHIP_ID_W3200: printk("w3200"); break;
506 case CHIP_ID_W3220: printk("w3220"); break;
507 default:
508 printk("Unknown imageon chip ID\n");
509 err = -ENODEV;
510 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700512 printk(" at 0x%08lx.\n", mem->start+W100_CFG_BASE);
513
514 /* Remap the framebuffer */
515 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
516 if (remapped_fbuf == NULL)
517 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
520 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700521 err = -ENOMEM;
522 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 }
524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 dev_set_drvdata(dev, info);
527
528 inf = dev->platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700529 par->chip_id = chip_id;
530 par->mach = inf;
531 par->fastpll_mode = 0;
532 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Richard Purdieaac51f02005-09-06 15:19:03 -0700534 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
535 if (!par->pll_table) {
536 printk(KERN_ERR "No matching Xtal definition found\n");
537 err = -EINVAL;
538 goto out;
539 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
542 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700543 err = -ENOMEM;
544 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
546
547 info->fbops = &w100fb_ops;
548 info->flags = FBINFO_DEFAULT;
549 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700550 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 info->screen_size = REMAPPED_FB_LEN;
552
Richard Purdieaac51f02005-09-06 15:19:03 -0700553 strcpy(info->fix.id, "w100fb");
554 info->fix.type = FB_TYPE_PACKED_PIXELS;
555 info->fix.type_aux = 0;
556 info->fix.accel = FB_ACCEL_NONE;
557 info->fix.smem_start = mem->start+W100_FB_BASE;
558 info->fix.mmio_start = mem->start+W100_REG_BASE;
559 info->fix.mmio_len = W100_REG_LEN;
560
561 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
562 err = -ENOMEM;
563 goto out;
564 }
565
566 par->mode = &inf->modelist[0];
567 if(inf->init_mode & INIT_MODE_ROTATED) {
568 info->var.xres = par->mode->yres;
569 info->var.yres = par->mode->xres;
570 }
571 else {
572 info->var.xres = par->mode->xres;
573 info->var.yres = par->mode->yres;
574 }
575
576 if(inf->init_mode &= INIT_MODE_FLIPPED)
577 par->flip = 1;
578 else
579 par->flip = 0;
580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700583 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 info->var.sync = 0;
585 info->var.grayscale = 0;
586 info->var.xoffset = info->var.yoffset = 0;
587 info->var.accel_flags = 0;
588 info->var.activate = FB_ACTIVATE_NOW;
589
Richard Purdieaac51f02005-09-06 15:19:03 -0700590 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Richard Purdieaac51f02005-09-06 15:19:03 -0700592 if (w100fb_check_var(&info->var, info) < 0) {
593 err = -EINVAL;
594 goto out;
595 }
596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 w100fb_set_par(info);
598
599 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700600 err = -EINVAL;
601 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 }
603
Richard Purdieaac51f02005-09-06 15:19:03 -0700604 device_create_file(dev, &dev_attr_fastpllclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 device_create_file(dev, &dev_attr_reg_read);
606 device_create_file(dev, &dev_attr_reg_write);
Richard Purdieaac51f02005-09-06 15:19:03 -0700607 device_create_file(dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
610 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700611out:
612 fb_dealloc_cmap(&info->cmap);
613 kfree(info->pseudo_palette);
614 if (remapped_fbuf != NULL)
615 iounmap(remapped_fbuf);
616 if (remapped_regs != NULL)
617 iounmap(remapped_regs);
618 if (remapped_base != NULL)
619 iounmap(remapped_base);
620 if (info)
621 framebuffer_release(info);
622 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623}
624
625
626static int w100fb_remove(struct device *dev)
627{
628 struct fb_info *info = dev_get_drvdata(dev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700629 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Richard Purdieaac51f02005-09-06 15:19:03 -0700631 device_remove_file(dev, &dev_attr_fastpllclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 device_remove_file(dev, &dev_attr_reg_read);
633 device_remove_file(dev, &dev_attr_reg_write);
Richard Purdieaac51f02005-09-06 15:19:03 -0700634 device_remove_file(dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 unregister_framebuffer(info);
637
Richard Purdieaac51f02005-09-06 15:19:03 -0700638 vfree(par->saved_intmem);
639 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700641 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 iounmap(remapped_base);
644 iounmap(remapped_regs);
645 iounmap(remapped_fbuf);
646
647 framebuffer_release(info);
648
649 return 0;
650}
651
652
653/* ------------------- chipset specific functions -------------------------- */
654
655
656static void w100_soft_reset(void)
657{
658 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
659 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
660 udelay(100);
661 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
662 udelay(100);
663}
664
Richard Purdieaac51f02005-09-06 15:19:03 -0700665static void w100_update_disable(void)
666{
667 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
668
669 /* Prevent display updates */
670 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
671 disp_db_buf_wr_cntl.f.update_db_buf = 0;
672 disp_db_buf_wr_cntl.f.en_db_buf = 0;
673 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
674}
675
676static void w100_update_enable(void)
677{
678 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
679
680 /* Enable display updates */
681 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
682 disp_db_buf_wr_cntl.f.update_db_buf = 1;
683 disp_db_buf_wr_cntl.f.en_db_buf = 1;
684 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
685}
686
687unsigned long w100fb_gpio_read(int port)
688{
689 unsigned long value;
690
691 if (port==W100_GPIO_PORT_A)
692 value = readl(remapped_regs + mmGPIO_DATA);
693 else
694 value = readl(remapped_regs + mmGPIO_DATA2);
695
696 return value;
697}
698
699void w100fb_gpio_write(int port, unsigned long value)
700{
701 if (port==W100_GPIO_PORT_A)
702 value = writel(value, remapped_regs + mmGPIO_DATA);
703 else
704 value = writel(value, remapped_regs + mmGPIO_DATA2);
705}
706EXPORT_SYMBOL(w100fb_gpio_read);
707EXPORT_SYMBOL(w100fb_gpio_write);
708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709/*
710 * Initialization of critical w100 hardware
711 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700712static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713{
714 u32 temp32;
715 union cif_cntl_u cif_cntl;
716 union intf_cntl_u intf_cntl;
717 union cfgreg_base_u cfgreg_base;
718 union wrap_top_dir_u wrap_top_dir;
719 union cif_read_dbg_u cif_read_dbg;
720 union cpu_defaults_u cpu_default;
721 union cif_write_dbg_u cif_write_dbg;
722 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700724 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
726 w100_soft_reset();
727
728 /* This is what the fpga_init code does on reset. May be wrong
729 but there is little info available */
730 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
731 for (temp32 = 0; temp32 < 10000; temp32++)
732 readl(remapped_regs + mmSCRATCH_UMSK);
733 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
734
735 /* Set up CIF */
736 cif_io.val = defCIF_IO;
737 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
738
739 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
740 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
741 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
742 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
743 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
744
745 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
746 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
747 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
748
749 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
750 cif_cntl.f.dis_system_bits = 1;
751 cif_cntl.f.dis_mr = 1;
752 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
753 cif_cntl.f.intb_oe = 1;
754 cif_cntl.f.interrupt_active_high = 1;
755 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
756
757 /* Setup cfgINTF_CNTL and cfgCPU defaults */
758 intf_cntl.val = defINTF_CNTL;
759 intf_cntl.f.ad_inc_a = 1;
760 intf_cntl.f.ad_inc_b = 1;
761 intf_cntl.f.rd_data_rdy_a = 0;
762 intf_cntl.f.rd_data_rdy_b = 0;
763 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
764
765 cpu_default.val = defCPU_DEFAULTS;
766 cpu_default.f.access_ind_addr_a = 1;
767 cpu_default.f.access_ind_addr_b = 1;
768 cpu_default.f.access_scratch_reg = 1;
769 cpu_default.f.transition_size = 0;
770 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
771
772 /* set up the apertures */
773 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
774
775 cfgreg_base.val = defCFGREG_BASE;
776 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
777 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 wrap_start_dir.val = defWRAP_START_DIR;
780 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
781 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
782
783 wrap_top_dir.val = defWRAP_TOP_DIR;
784 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
785 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
786
787 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700788
789 /* Set the hardware to 565 colour */
790 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
791 temp32 &= 0xff7fffff;
792 temp32 |= 0x00800000;
793 writel(temp32, remapped_regs + mmDISP_DEBUG2);
794
795 /* Initialise the GPIO lines */
796 if (gpio) {
797 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
798 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
799 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
800 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
801 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
802 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
803 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
805
806
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807struct power_state {
808 union clk_pin_cntl_u clk_pin_cntl;
809 union pll_ref_fb_div_u pll_ref_fb_div;
810 union pll_cntl_u pll_cntl;
811 union sclk_cntl_u sclk_cntl;
812 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700814 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815};
816
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818static struct power_state w100_pwr_state;
819
Richard Purdieaac51f02005-09-06 15:19:03 -0700820/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
821
822/* 12.5MHz Crystal PLL Table */
823static struct w100_pll_info xtal_12500000[] = {
824 /*freq M N_int N_fac tfgoal lock_time */
825 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
826 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
827 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
828 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
829 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
830 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831};
832
Richard Purdieaac51f02005-09-06 15:19:03 -0700833/* 14.318MHz Crystal PLL Table */
834static struct w100_pll_info xtal_14318000[] = {
835 /*freq M N_int N_fac tfgoal lock_time */
836 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
837 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
838 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
839 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
840 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
841 { 0, 0, 0, 0, 0, 0},
842};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Richard Purdieaac51f02005-09-06 15:19:03 -0700844/* 16MHz Crystal PLL Table */
845static struct w100_pll_info xtal_16000000[] = {
846 /*freq M N_int N_fac tfgoal lock_time */
847 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
848 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
849 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
850 { 0, 0, 0, 0, 0, 0},
851};
852
853static struct pll_entries {
854 int xtal_freq;
855 struct w100_pll_info *pll_table;
856} w100_pll_tables[] = {
857 { 12500000, &xtal_12500000[0] },
858 { 14318000, &xtal_14318000[0] },
859 { 16000000, &xtal_16000000[0] },
860 { 0 },
861};
862
863struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Richard Purdieaac51f02005-09-06 15:19:03 -0700865 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Richard Purdieaac51f02005-09-06 15:19:03 -0700867 do {
868 if (freq == pll_entry->xtal_freq)
869 return pll_entry->pll_table;
870 pll_entry++;
871 } while (pll_entry->xtal_freq);
872 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873}
874
875
Richard Purdieaac51f02005-09-06 15:19:03 -0700876static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
Richard Purdieaac51f02005-09-06 15:19:03 -0700878 union clk_test_cntl_u clk_test_cntl;
879
880 udelay(5);
881
882 /* Select the test clock source and reset */
883 clk_test_cntl.f.start_check_freq = 0x0;
884 clk_test_cntl.f.testclk_sel = testclk_sel;
885 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
886 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
887
888 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
889 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
890
891 /* Run clock test */
892 clk_test_cntl.f.start_check_freq = 0x1;
893 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
894
895 /* Give the test time to complete */
896 udelay(20);
897
898 /* Return the result */
899 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
900 clk_test_cntl.f.start_check_freq = 0x0;
901 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
902
903 return clk_test_cntl.f.test_count;
904}
905
906
907static int w100_pll_adjust(struct w100_pll_info *pll)
908{
909 unsigned int tf80;
910 unsigned int tf20;
911
912 /* Initial Settings */
913 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
914 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
915 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
916 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
917 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
918 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
919 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
920
921 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
922 * therefore, commented out the following lines
923 * tf80 meant tf100
924 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 do {
Richard Purdieaac51f02005-09-06 15:19:03 -0700926 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
928 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
929
Richard Purdieaac51f02005-09-06 15:19:03 -0700930 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
931 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 /* set VCO input = 0.2 * VDD */
933 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
934 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
935
Richard Purdieaac51f02005-09-06 15:19:03 -0700936 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
937 if (tf20 <= (pll->tfgoal))
938 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
940 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -0700941 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
942 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 /* slow VCO config */
944 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
945 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
946 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 continue;
948 }
949 }
950 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
951 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700952 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
954 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700955 } else {
956 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 } while(1);
959}
960
961
962/*
963 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700965static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
Richard Purdieaac51f02005-09-06 15:19:03 -0700967 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Richard Purdieaac51f02005-09-06 15:19:03 -0700969 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 /* set VCO input = 0.5 * VDD */
973 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
974 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
975
Richard Purdieaac51f02005-09-06 15:19:03 -0700976 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -0700979 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
981
Richard Purdieaac51f02005-09-06 15:19:03 -0700982 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
984 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
985
Richard Purdieaac51f02005-09-06 15:19:03 -0700986 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
988 /* PLL locked */
989
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 return status;
991}
992
993
Richard Purdieaac51f02005-09-06 15:19:03 -0700994static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Richard Purdieaac51f02005-09-06 15:19:03 -0700996 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Richard Purdieaac51f02005-09-06 15:19:03 -0700998 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001000 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
1001 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1003 }
1004
Richard Purdieaac51f02005-09-06 15:19:03 -07001005 /* Set system clock source to XTAL whilst adjusting the PLL! */
1006 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1008
Richard Purdieaac51f02005-09-06 15:19:03 -07001009 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1010 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1011 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1012 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1014
1015 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1016 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1017
Richard Purdieaac51f02005-09-06 15:19:03 -07001018 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
Richard Purdieaac51f02005-09-06 15:19:03 -07001020 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001022 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1023 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1025 }
1026 return status;
1027}
1028
Richard Purdieaac51f02005-09-06 15:19:03 -07001029/* freq = target frequency of the PLL */
1030static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031{
Richard Purdieaac51f02005-09-06 15:19:03 -07001032 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033
Richard Purdieaac51f02005-09-06 15:19:03 -07001034 do {
1035 if (freq == pll->freq) {
1036 return w100_pll_set_clk(pll);
1037 }
1038 pll++;
1039 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 return 0;
1041}
1042
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043/* Set up an initial state. Some values/fields set
1044 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001045static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046{
1047 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1048 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1049 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1050 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001051 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1053 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1054
Richard Purdieaac51f02005-09-06 15:19:03 -07001055 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1056 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001058 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001060 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1061 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1062 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1063 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1064 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1065 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1066 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1067 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1068 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1070 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1071 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1072 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1073 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1074
Richard Purdieaac51f02005-09-06 15:19:03 -07001075 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1076 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1077 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1079
Richard Purdieaac51f02005-09-06 15:19:03 -07001080 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1081 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1083 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1084 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1085 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1086
1087 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1088 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1089 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001090 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1092 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1093 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1094 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1095 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1096 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1097 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1098 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1099 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001100 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1102 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1103 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1104 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1105 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001108 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1110 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1111 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001112 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1113 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1115 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1116 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1117
Richard Purdieaac51f02005-09-06 15:19:03 -07001118 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119}
1120
1121
Richard Purdieaac51f02005-09-06 15:19:03 -07001122/*
1123 * Setup the w100 clocks for the specified mode
1124 */
1125static void w100_init_clocks(struct w100fb_par *par)
1126{
1127 struct w100_mode *mode = par->mode;
1128
1129 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1130 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1131
1132 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1133 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1134 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1135 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1136}
1137
1138static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139{
1140 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001141 struct w100_mode *mode = par->mode;
1142 struct w100_gen_regs *regs = par->mach->regs;
1143 union active_h_disp_u active_h_disp;
1144 union active_v_disp_u active_v_disp;
1145 union graphic_h_disp_u graphic_h_disp;
1146 union graphic_v_disp_u graphic_v_disp;
1147 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148
Richard Purdieaac51f02005-09-06 15:19:03 -07001149 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
Richard Purdieaac51f02005-09-06 15:19:03 -07001151 active_h_disp.val = 0;
1152 active_h_disp.f.active_h_start=mode->left_margin;
1153 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1154 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
Richard Purdieaac51f02005-09-06 15:19:03 -07001156 active_v_disp.val = 0;
1157 active_v_disp.f.active_v_start=mode->upper_margin;
1158 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1159 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
Richard Purdieaac51f02005-09-06 15:19:03 -07001161 graphic_h_disp.val = 0;
1162 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1163 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1164 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1165
1166 graphic_v_disp.val = 0;
1167 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1168 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1169 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1170
1171 crtc_total.val = 0;
1172 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1173 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1174 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1175
1176 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1177 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1178 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1179 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1180 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1181 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1182 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1183 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1184 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1185
1186 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1187 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1188 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1189 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1190 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1191 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1192
1193 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1194 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1195 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1196 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197
1198 /* Hack for overlay in ext memory */
1199 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1200 temp32 |= 0xc0000000;
1201 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202}
1203
1204
Richard Purdieaac51f02005-09-06 15:19:03 -07001205static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206{
Richard Purdieaac51f02005-09-06 15:19:03 -07001207 union mc_ext_mem_location_u extmem_location;
1208 union mc_fb_location_u intmem_location;
1209 struct w100_mem_info *mem = par->mach->mem;
1210 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1211
1212 if (!par->extmem_active) {
1213 w100_suspend(W100_SUSPEND_EXTMEM);
1214
1215 /* Map Internal Memory at FB Base */
1216 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1217 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1218 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1219
1220 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1221 to acceleration libraries */
1222 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1223 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1224 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1225 } else {
1226 /* Map Internal Memory to its default location */
1227 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1228 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1229 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1230
1231 /* Map External Memory at FB Base */
1232 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1233 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1234 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1235
1236 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1237 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1238 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1239 udelay(100);
1240 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1241 udelay(100);
1242 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1243 udelay(100);
1244 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1245 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1246 if (bm_mem) {
1247 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1248 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1249 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1250 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1251 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1252 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1253 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1254 }
1255 }
1256}
1257
1258static void w100_set_dispregs(struct w100fb_par *par)
1259{
1260 unsigned long rot=0, divider, offset=0;
1261 union graphic_ctrl_u graphic_ctrl;
1262
1263 /* See if the mode has been rotated */
1264 if (par->xres == par->mode->xres) {
1265 if (par->flip) {
1266 rot=3; /* 180 degree */
1267 offset=(par->xres * par->yres) - 1;
1268 } /* else 0 degree */
1269 divider = par->mode->pixclk_divider;
1270 } else {
1271 if (par->flip) {
1272 rot=2; /* 270 degree */
1273 offset=par->xres - 1;
1274 } else {
1275 rot=1; /* 90 degree */
1276 offset=par->xres * (par->yres - 1);
1277 }
1278 divider = par->mode->pixclk_divider_rotated;
1279 }
1280
1281 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1282 switch (par->chip_id) {
1283 case CHIP_ID_W100:
1284 graphic_ctrl.f_w100.color_depth=6;
1285 graphic_ctrl.f_w100.en_crtc=1;
1286 graphic_ctrl.f_w100.en_graphic_req=1;
1287 graphic_ctrl.f_w100.en_graphic_crtc=1;
1288 graphic_ctrl.f_w100.lcd_pclk_on=1;
1289 graphic_ctrl.f_w100.lcd_sclk_on=1;
1290 graphic_ctrl.f_w100.low_power_on=0;
1291 graphic_ctrl.f_w100.req_freq=0;
1292 graphic_ctrl.f_w100.portrait_mode=rot;
1293
1294 /* Zaurus needs this */
1295 switch(par->xres) {
1296 case 240:
1297 case 320:
1298 default:
1299 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1300 break;
1301 case 480:
1302 case 640:
1303 switch(rot) {
1304 case 0: /* 0 */
1305 case 3: /* 180 */
1306 graphic_ctrl.f_w100.low_power_on=1;
1307 graphic_ctrl.f_w100.req_freq=5;
1308 break;
1309 case 1: /* 90 */
1310 case 2: /* 270 */
1311 graphic_ctrl.f_w100.req_freq=4;
1312 break;
1313 default:
1314 break;
1315 }
1316 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1317 break;
1318 }
1319 break;
1320 case CHIP_ID_W3200:
1321 case CHIP_ID_W3220:
1322 graphic_ctrl.f_w32xx.color_depth=6;
1323 graphic_ctrl.f_w32xx.en_crtc=1;
1324 graphic_ctrl.f_w32xx.en_graphic_req=1;
1325 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1326 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1327 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1328 graphic_ctrl.f_w32xx.low_power_on=0;
1329 graphic_ctrl.f_w32xx.req_freq=0;
1330 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1331 graphic_ctrl.f_w32xx.portrait_mode=rot;
1332 break;
1333 }
1334
1335 /* Set the pixel clock source and divider */
1336 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1338 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1339
Richard Purdieaac51f02005-09-06 15:19:03 -07001340 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1341 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1342 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343}
1344
1345
Richard Purdieaac51f02005-09-06 15:19:03 -07001346/*
1347 * Work out how long the sync pulse lasts
1348 * Value is 1/(time in seconds)
1349 */
1350static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351{
Richard Purdieaac51f02005-09-06 15:19:03 -07001352 unsigned long hsync;
1353 struct w100_mode *mode = par->mode;
1354 union crtc_ss_u crtc_ss;
1355
1356 if (mode->pixclk_src == CLK_SRC_XTAL)
1357 hsync=par->mach->xtal_freq;
1358 else
1359 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1360
1361 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1362
1363 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1364 if (crtc_ss.val)
1365 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1366 else
1367 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368}
1369
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370static void w100_suspend(u32 mode)
1371{
1372 u32 val;
1373
1374 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1375 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1376
1377 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001378 val &= ~(0x00100000); /* bit20=0 */
1379 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1381
1382 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001383 val &= ~(0x00040000); /* bit18=0 */
1384 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1386
Richard Purdieaac51f02005-09-06 15:19:03 -07001387 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 /* CKE: Tri-State */
1391 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001392 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1394
1395 /* CLK: Stop */
1396 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001397 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1399 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1401 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1402 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1403
1404 udelay(5);
1405
1406 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001407 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 writel(val, remapped_regs + mmPLL_CNTL);
1409 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1410 }
1411}
1412
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413static void w100_vsync(void)
1414{
1415 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001416 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
1418 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1419
1420 /* set vline pos */
1421 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1422
1423 /* disable vline irq */
1424 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1425
1426 tmp &= ~0x00000002;
1427 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1428
1429 /* clear vline irq status */
1430 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1431
1432 /* enable vline irq */
1433 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1434
1435 /* clear vline irq status */
1436 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1437
1438 while(timeout > 0) {
1439 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1440 break;
1441 udelay(1);
1442 timeout--;
1443 }
1444
1445 /* disable vline irq */
1446 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1447
1448 /* clear vline irq status */
1449 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1450}
1451
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452static struct device_driver w100fb_driver = {
1453 .name = "w100fb",
1454 .bus = &platform_bus_type,
1455 .probe = w100fb_probe,
1456 .remove = w100fb_remove,
1457 .suspend = w100fb_suspend,
1458 .resume = w100fb_resume,
1459};
1460
1461int __devinit w100fb_init(void)
1462{
1463 return driver_register(&w100fb_driver);
1464}
1465
1466void __exit w100fb_cleanup(void)
1467{
1468 driver_unregister(&w100fb_driver);
1469}
1470
1471module_init(w100fb_init);
1472module_exit(w100fb_cleanup);
1473
1474MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001475MODULE_LICENSE("GPL");