blob: f6e24ee85f077db2e0f6d3021aef12a652f6e383 [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>
Russell Kingd052d1b2005-10-29 19:07:23 +010028#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#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,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400};
401
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402#ifdef CONFIG_PM
Richard Purdieaac51f02005-09-06 15:19:03 -0700403static void w100fb_save_vidmem(struct w100fb_par *par)
404{
405 int memsize;
406
407 if (par->extmem_active) {
408 memsize=par->mach->mem->size;
409 par->saved_extmem = vmalloc(memsize);
410 if (par->saved_extmem)
411 memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
412 }
413 memsize=MEM_INT_SIZE;
414 par->saved_intmem = vmalloc(memsize);
415 if (par->saved_intmem && par->extmem_active)
416 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
417 else if (par->saved_intmem)
418 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
419}
420
421static void w100fb_restore_vidmem(struct w100fb_par *par)
422{
423 int memsize;
424
425 if (par->extmem_active && par->saved_extmem) {
426 memsize=par->mach->mem->size;
427 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
428 vfree(par->saved_extmem);
429 }
430 if (par->saved_intmem) {
431 memsize=MEM_INT_SIZE;
432 if (par->extmem_active)
433 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
434 else
435 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
436 vfree(par->saved_intmem);
437 }
438}
439
Russell King3ae5eae2005-11-09 22:32:44 +0000440static int w100fb_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441{
Russell King3ae5eae2005-11-09 22:32:44 +0000442 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700443 struct w100fb_par *par=info->par;
444 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
Russell King9480e302005-10-28 09:52:56 -0700446 w100fb_save_vidmem(par);
447 if(tg && tg->suspend)
448 tg->suspend(par);
449 w100_suspend(W100_SUSPEND_ALL);
450 par->blanked = 1;
451
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 return 0;
453}
454
Russell King3ae5eae2005-11-09 22:32:44 +0000455static int w100fb_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Russell King3ae5eae2005-11-09 22:32:44 +0000457 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700458 struct w100fb_par *par=info->par;
459 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
Russell King9480e302005-10-28 09:52:56 -0700461 w100_hw_init(par);
462 w100fb_activate_var(par);
463 w100fb_restore_vidmem(par);
464 if(tg && tg->resume)
465 tg->resume(par);
466 par->blanked = 0;
467
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 return 0;
469}
470#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700471#define w100fb_suspend NULL
472#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473#endif
474
475
Russell King3ae5eae2005-11-09 22:32:44 +0000476int __init w100fb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
Richard Purdieaac51f02005-09-06 15:19:03 -0700478 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700480 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 struct w100fb_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700483 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485 if (!mem)
486 return -EINVAL;
487
Richard Purdieaac51f02005-09-06 15:19:03 -0700488 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
490 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700491 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Richard Purdieaac51f02005-09-06 15:19:03 -0700493 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700495 if (remapped_regs == NULL)
496 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Richard Purdieaac51f02005-09-06 15:19:03 -0700498 /* Identify the chip */
499 printk("Found ");
500 chip_id = readl(remapped_regs + mmCHIP_ID);
501 switch(chip_id) {
502 case CHIP_ID_W100: printk("w100"); break;
503 case CHIP_ID_W3200: printk("w3200"); break;
504 case CHIP_ID_W3220: printk("w3220"); break;
505 default:
506 printk("Unknown imageon chip ID\n");
507 err = -ENODEV;
508 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700510 printk(" at 0x%08lx.\n", mem->start+W100_CFG_BASE);
511
512 /* Remap the framebuffer */
513 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
514 if (remapped_fbuf == NULL)
515 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Richard Purdieafdd3b32005-11-13 16:06:38 -0800517 info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700519 err = -ENOMEM;
520 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 }
522
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 par = info->par;
Russell King3ae5eae2005-11-09 22:32:44 +0000524 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Russell King3ae5eae2005-11-09 22:32:44 +0000526 inf = pdev->dev.platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700527 par->chip_id = chip_id;
528 par->mach = inf;
529 par->fastpll_mode = 0;
530 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
Richard Purdieaac51f02005-09-06 15:19:03 -0700532 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
533 if (!par->pll_table) {
534 printk(KERN_ERR "No matching Xtal definition found\n");
535 err = -EINVAL;
536 goto out;
537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
540 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700541 err = -ENOMEM;
542 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
544
545 info->fbops = &w100fb_ops;
546 info->flags = FBINFO_DEFAULT;
547 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700548 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 info->screen_size = REMAPPED_FB_LEN;
550
Richard Purdieaac51f02005-09-06 15:19:03 -0700551 strcpy(info->fix.id, "w100fb");
552 info->fix.type = FB_TYPE_PACKED_PIXELS;
553 info->fix.type_aux = 0;
554 info->fix.accel = FB_ACCEL_NONE;
555 info->fix.smem_start = mem->start+W100_FB_BASE;
556 info->fix.mmio_start = mem->start+W100_REG_BASE;
557 info->fix.mmio_len = W100_REG_LEN;
558
559 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
560 err = -ENOMEM;
561 goto out;
562 }
563
564 par->mode = &inf->modelist[0];
565 if(inf->init_mode & INIT_MODE_ROTATED) {
566 info->var.xres = par->mode->yres;
567 info->var.yres = par->mode->xres;
568 }
569 else {
570 info->var.xres = par->mode->xres;
571 info->var.yres = par->mode->yres;
572 }
573
574 if(inf->init_mode &= INIT_MODE_FLIPPED)
575 par->flip = 1;
576 else
577 par->flip = 0;
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700581 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 info->var.sync = 0;
583 info->var.grayscale = 0;
584 info->var.xoffset = info->var.yoffset = 0;
585 info->var.accel_flags = 0;
586 info->var.activate = FB_ACTIVATE_NOW;
587
Richard Purdieaac51f02005-09-06 15:19:03 -0700588 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Richard Purdieaac51f02005-09-06 15:19:03 -0700590 if (w100fb_check_var(&info->var, info) < 0) {
591 err = -EINVAL;
592 goto out;
593 }
594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 w100fb_set_par(info);
596
597 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700598 err = -EINVAL;
599 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 }
601
Russell King3ae5eae2005-11-09 22:32:44 +0000602 device_create_file(&pdev->dev, &dev_attr_fastpllclk);
603 device_create_file(&pdev->dev, &dev_attr_reg_read);
604 device_create_file(&pdev->dev, &dev_attr_reg_write);
605 device_create_file(&pdev->dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
608 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700609out:
610 fb_dealloc_cmap(&info->cmap);
611 kfree(info->pseudo_palette);
612 if (remapped_fbuf != NULL)
613 iounmap(remapped_fbuf);
614 if (remapped_regs != NULL)
615 iounmap(remapped_regs);
616 if (remapped_base != NULL)
617 iounmap(remapped_base);
618 if (info)
619 framebuffer_release(info);
620 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621}
622
623
Russell King3ae5eae2005-11-09 22:32:44 +0000624static int w100fb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
Russell King3ae5eae2005-11-09 22:32:44 +0000626 struct fb_info *info = platform_get_drvdata(pdev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700627 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Russell King3ae5eae2005-11-09 22:32:44 +0000629 device_remove_file(&pdev->dev, &dev_attr_fastpllclk);
630 device_remove_file(&pdev->dev, &dev_attr_reg_read);
631 device_remove_file(&pdev->dev, &dev_attr_reg_write);
632 device_remove_file(&pdev->dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634 unregister_framebuffer(info);
635
Richard Purdieaac51f02005-09-06 15:19:03 -0700636 vfree(par->saved_intmem);
637 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700639 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 iounmap(remapped_base);
642 iounmap(remapped_regs);
643 iounmap(remapped_fbuf);
644
645 framebuffer_release(info);
646
647 return 0;
648}
649
650
651/* ------------------- chipset specific functions -------------------------- */
652
653
654static void w100_soft_reset(void)
655{
656 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
657 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
658 udelay(100);
659 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
660 udelay(100);
661}
662
Richard Purdieaac51f02005-09-06 15:19:03 -0700663static void w100_update_disable(void)
664{
665 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
666
667 /* Prevent display updates */
668 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
669 disp_db_buf_wr_cntl.f.update_db_buf = 0;
670 disp_db_buf_wr_cntl.f.en_db_buf = 0;
671 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
672}
673
674static void w100_update_enable(void)
675{
676 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
677
678 /* Enable display updates */
679 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
680 disp_db_buf_wr_cntl.f.update_db_buf = 1;
681 disp_db_buf_wr_cntl.f.en_db_buf = 1;
682 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
683}
684
685unsigned long w100fb_gpio_read(int port)
686{
687 unsigned long value;
688
689 if (port==W100_GPIO_PORT_A)
690 value = readl(remapped_regs + mmGPIO_DATA);
691 else
692 value = readl(remapped_regs + mmGPIO_DATA2);
693
694 return value;
695}
696
697void w100fb_gpio_write(int port, unsigned long value)
698{
699 if (port==W100_GPIO_PORT_A)
700 value = writel(value, remapped_regs + mmGPIO_DATA);
701 else
702 value = writel(value, remapped_regs + mmGPIO_DATA2);
703}
704EXPORT_SYMBOL(w100fb_gpio_read);
705EXPORT_SYMBOL(w100fb_gpio_write);
706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707/*
708 * Initialization of critical w100 hardware
709 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700710static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711{
712 u32 temp32;
713 union cif_cntl_u cif_cntl;
714 union intf_cntl_u intf_cntl;
715 union cfgreg_base_u cfgreg_base;
716 union wrap_top_dir_u wrap_top_dir;
717 union cif_read_dbg_u cif_read_dbg;
718 union cpu_defaults_u cpu_default;
719 union cif_write_dbg_u cif_write_dbg;
720 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700722 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 w100_soft_reset();
725
726 /* This is what the fpga_init code does on reset. May be wrong
727 but there is little info available */
728 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
729 for (temp32 = 0; temp32 < 10000; temp32++)
730 readl(remapped_regs + mmSCRATCH_UMSK);
731 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
732
733 /* Set up CIF */
734 cif_io.val = defCIF_IO;
735 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
736
737 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
738 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
739 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
740 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
741 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
742
743 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
744 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
745 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
746
747 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
748 cif_cntl.f.dis_system_bits = 1;
749 cif_cntl.f.dis_mr = 1;
750 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
751 cif_cntl.f.intb_oe = 1;
752 cif_cntl.f.interrupt_active_high = 1;
753 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
754
755 /* Setup cfgINTF_CNTL and cfgCPU defaults */
756 intf_cntl.val = defINTF_CNTL;
757 intf_cntl.f.ad_inc_a = 1;
758 intf_cntl.f.ad_inc_b = 1;
759 intf_cntl.f.rd_data_rdy_a = 0;
760 intf_cntl.f.rd_data_rdy_b = 0;
761 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
762
763 cpu_default.val = defCPU_DEFAULTS;
764 cpu_default.f.access_ind_addr_a = 1;
765 cpu_default.f.access_ind_addr_b = 1;
766 cpu_default.f.access_scratch_reg = 1;
767 cpu_default.f.transition_size = 0;
768 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
769
770 /* set up the apertures */
771 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
772
773 cfgreg_base.val = defCFGREG_BASE;
774 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
775 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 wrap_start_dir.val = defWRAP_START_DIR;
778 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
779 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
780
781 wrap_top_dir.val = defWRAP_TOP_DIR;
782 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
783 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
784
785 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700786
787 /* Set the hardware to 565 colour */
788 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
789 temp32 &= 0xff7fffff;
790 temp32 |= 0x00800000;
791 writel(temp32, remapped_regs + mmDISP_DEBUG2);
792
793 /* Initialise the GPIO lines */
794 if (gpio) {
795 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
796 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
797 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
798 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
799 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
800 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
801 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802}
803
804
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805struct power_state {
806 union clk_pin_cntl_u clk_pin_cntl;
807 union pll_ref_fb_div_u pll_ref_fb_div;
808 union pll_cntl_u pll_cntl;
809 union sclk_cntl_u sclk_cntl;
810 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700812 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813};
814
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816static struct power_state w100_pwr_state;
817
Richard Purdieaac51f02005-09-06 15:19:03 -0700818/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
819
820/* 12.5MHz Crystal PLL Table */
821static struct w100_pll_info xtal_12500000[] = {
822 /*freq M N_int N_fac tfgoal lock_time */
823 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
824 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
825 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
826 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
827 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
828 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829};
830
Richard Purdieaac51f02005-09-06 15:19:03 -0700831/* 14.318MHz Crystal PLL Table */
832static struct w100_pll_info xtal_14318000[] = {
833 /*freq M N_int N_fac tfgoal lock_time */
834 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
835 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
836 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
837 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
838 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
839 { 0, 0, 0, 0, 0, 0},
840};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
Richard Purdieaac51f02005-09-06 15:19:03 -0700842/* 16MHz Crystal PLL Table */
843static struct w100_pll_info xtal_16000000[] = {
844 /*freq M N_int N_fac tfgoal lock_time */
845 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
846 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
847 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
848 { 0, 0, 0, 0, 0, 0},
849};
850
851static struct pll_entries {
852 int xtal_freq;
853 struct w100_pll_info *pll_table;
854} w100_pll_tables[] = {
855 { 12500000, &xtal_12500000[0] },
856 { 14318000, &xtal_14318000[0] },
857 { 16000000, &xtal_16000000[0] },
858 { 0 },
859};
860
861struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Richard Purdieaac51f02005-09-06 15:19:03 -0700863 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Richard Purdieaac51f02005-09-06 15:19:03 -0700865 do {
866 if (freq == pll_entry->xtal_freq)
867 return pll_entry->pll_table;
868 pll_entry++;
869 } while (pll_entry->xtal_freq);
870 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871}
872
873
Richard Purdieaac51f02005-09-06 15:19:03 -0700874static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875{
Richard Purdieaac51f02005-09-06 15:19:03 -0700876 union clk_test_cntl_u clk_test_cntl;
877
878 udelay(5);
879
880 /* Select the test clock source and reset */
881 clk_test_cntl.f.start_check_freq = 0x0;
882 clk_test_cntl.f.testclk_sel = testclk_sel;
883 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
884 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
885
886 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
887 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
888
889 /* Run clock test */
890 clk_test_cntl.f.start_check_freq = 0x1;
891 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
892
893 /* Give the test time to complete */
894 udelay(20);
895
896 /* Return the result */
897 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
898 clk_test_cntl.f.start_check_freq = 0x0;
899 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
900
901 return clk_test_cntl.f.test_count;
902}
903
904
905static int w100_pll_adjust(struct w100_pll_info *pll)
906{
907 unsigned int tf80;
908 unsigned int tf20;
909
910 /* Initial Settings */
911 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
912 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
913 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
914 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
915 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
916 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
917 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
918
919 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
920 * therefore, commented out the following lines
921 * tf80 meant tf100
922 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 do {
Richard Purdieaac51f02005-09-06 15:19:03 -0700924 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
926 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
927
Richard Purdieaac51f02005-09-06 15:19:03 -0700928 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
929 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 /* set VCO input = 0.2 * VDD */
931 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
932 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
933
Richard Purdieaac51f02005-09-06 15:19:03 -0700934 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
935 if (tf20 <= (pll->tfgoal))
936 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -0700939 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
940 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 /* slow VCO config */
942 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
943 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
944 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 continue;
946 }
947 }
948 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
949 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700950 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
952 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700953 } else {
954 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 } while(1);
957}
958
959
960/*
961 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700963static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964{
Richard Purdieaac51f02005-09-06 15:19:03 -0700965 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Richard Purdieaac51f02005-09-06 15:19:03 -0700967 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 /* set VCO input = 0.5 * VDD */
971 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
972 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
973
Richard Purdieaac51f02005-09-06 15:19:03 -0700974 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -0700977 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
979
Richard Purdieaac51f02005-09-06 15:19:03 -0700980 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
982 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
983
Richard Purdieaac51f02005-09-06 15:19:03 -0700984 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
986 /* PLL locked */
987
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return status;
989}
990
991
Richard Purdieaac51f02005-09-06 15:19:03 -0700992static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993{
Richard Purdieaac51f02005-09-06 15:19:03 -0700994 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Richard Purdieaac51f02005-09-06 15:19:03 -0700996 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 {
Richard Purdieaac51f02005-09-06 15:19:03 -0700998 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
999 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1001 }
1002
Richard Purdieaac51f02005-09-06 15:19:03 -07001003 /* Set system clock source to XTAL whilst adjusting the PLL! */
1004 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1006
Richard Purdieaac51f02005-09-06 15:19:03 -07001007 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1008 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1009 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1010 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1012
1013 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1014 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1015
Richard Purdieaac51f02005-09-06 15:19:03 -07001016 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
Richard Purdieaac51f02005-09-06 15:19:03 -07001018 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001020 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1021 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1023 }
1024 return status;
1025}
1026
Richard Purdieaac51f02005-09-06 15:19:03 -07001027/* freq = target frequency of the PLL */
1028static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029{
Richard Purdieaac51f02005-09-06 15:19:03 -07001030 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
Richard Purdieaac51f02005-09-06 15:19:03 -07001032 do {
1033 if (freq == pll->freq) {
1034 return w100_pll_set_clk(pll);
1035 }
1036 pll++;
1037 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 return 0;
1039}
1040
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041/* Set up an initial state. Some values/fields set
1042 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001043static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044{
1045 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1046 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1047 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1048 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001049 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1051 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1052
Richard Purdieaac51f02005-09-06 15:19:03 -07001053 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1054 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001056 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001058 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1059 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1060 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1061 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1062 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1063 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1064 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1065 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1066 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1068 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1069 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1070 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1071 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1072
Richard Purdieaac51f02005-09-06 15:19:03 -07001073 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1074 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1075 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1077
Richard Purdieaac51f02005-09-06 15:19:03 -07001078 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1079 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1081 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1082 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1083 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1084
1085 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1086 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1087 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001088 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1090 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1091 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1092 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1093 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1094 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1095 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1096 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1097 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001098 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1100 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1101 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1102 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1103 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001106 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1108 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1109 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001110 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1111 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1113 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1114 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1115
Richard Purdieaac51f02005-09-06 15:19:03 -07001116 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117}
1118
1119
Richard Purdieaac51f02005-09-06 15:19:03 -07001120/*
1121 * Setup the w100 clocks for the specified mode
1122 */
1123static void w100_init_clocks(struct w100fb_par *par)
1124{
1125 struct w100_mode *mode = par->mode;
1126
1127 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1128 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1129
1130 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1131 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1132 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1133 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1134}
1135
1136static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137{
1138 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001139 struct w100_mode *mode = par->mode;
1140 struct w100_gen_regs *regs = par->mach->regs;
1141 union active_h_disp_u active_h_disp;
1142 union active_v_disp_u active_v_disp;
1143 union graphic_h_disp_u graphic_h_disp;
1144 union graphic_v_disp_u graphic_v_disp;
1145 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
Richard Purdieaac51f02005-09-06 15:19:03 -07001147 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148
Richard Purdieaac51f02005-09-06 15:19:03 -07001149 active_h_disp.val = 0;
1150 active_h_disp.f.active_h_start=mode->left_margin;
1151 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1152 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153
Richard Purdieaac51f02005-09-06 15:19:03 -07001154 active_v_disp.val = 0;
1155 active_v_disp.f.active_v_start=mode->upper_margin;
1156 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1157 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Richard Purdieaac51f02005-09-06 15:19:03 -07001159 graphic_h_disp.val = 0;
1160 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1161 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1162 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1163
1164 graphic_v_disp.val = 0;
1165 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1166 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1167 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1168
1169 crtc_total.val = 0;
1170 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1171 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1172 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1173
1174 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1175 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1176 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1177 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1178 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1179 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1180 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1181 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1182 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1183
1184 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1185 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1186 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1187 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1188 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1189 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1190
1191 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1192 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1193 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1194 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 /* Hack for overlay in ext memory */
1197 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1198 temp32 |= 0xc0000000;
1199 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200}
1201
1202
Richard Purdieaac51f02005-09-06 15:19:03 -07001203static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204{
Richard Purdieaac51f02005-09-06 15:19:03 -07001205 union mc_ext_mem_location_u extmem_location;
1206 union mc_fb_location_u intmem_location;
1207 struct w100_mem_info *mem = par->mach->mem;
1208 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1209
1210 if (!par->extmem_active) {
1211 w100_suspend(W100_SUSPEND_EXTMEM);
1212
1213 /* Map Internal Memory at FB Base */
1214 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1215 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1216 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1217
1218 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1219 to acceleration libraries */
1220 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1221 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1222 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1223 } else {
1224 /* Map Internal Memory to its default location */
1225 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1226 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1227 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1228
1229 /* Map External Memory at FB Base */
1230 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1231 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1232 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1233
1234 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1235 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1236 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1237 udelay(100);
1238 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1239 udelay(100);
1240 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1241 udelay(100);
1242 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1243 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1244 if (bm_mem) {
1245 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1246 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1247 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1248 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1249 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1250 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1251 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1252 }
1253 }
1254}
1255
1256static void w100_set_dispregs(struct w100fb_par *par)
1257{
1258 unsigned long rot=0, divider, offset=0;
1259 union graphic_ctrl_u graphic_ctrl;
1260
1261 /* See if the mode has been rotated */
1262 if (par->xres == par->mode->xres) {
1263 if (par->flip) {
1264 rot=3; /* 180 degree */
1265 offset=(par->xres * par->yres) - 1;
1266 } /* else 0 degree */
1267 divider = par->mode->pixclk_divider;
1268 } else {
1269 if (par->flip) {
1270 rot=2; /* 270 degree */
1271 offset=par->xres - 1;
1272 } else {
1273 rot=1; /* 90 degree */
1274 offset=par->xres * (par->yres - 1);
1275 }
1276 divider = par->mode->pixclk_divider_rotated;
1277 }
1278
1279 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1280 switch (par->chip_id) {
1281 case CHIP_ID_W100:
1282 graphic_ctrl.f_w100.color_depth=6;
1283 graphic_ctrl.f_w100.en_crtc=1;
1284 graphic_ctrl.f_w100.en_graphic_req=1;
1285 graphic_ctrl.f_w100.en_graphic_crtc=1;
1286 graphic_ctrl.f_w100.lcd_pclk_on=1;
1287 graphic_ctrl.f_w100.lcd_sclk_on=1;
1288 graphic_ctrl.f_w100.low_power_on=0;
1289 graphic_ctrl.f_w100.req_freq=0;
1290 graphic_ctrl.f_w100.portrait_mode=rot;
1291
1292 /* Zaurus needs this */
1293 switch(par->xres) {
1294 case 240:
1295 case 320:
1296 default:
1297 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1298 break;
1299 case 480:
1300 case 640:
1301 switch(rot) {
1302 case 0: /* 0 */
1303 case 3: /* 180 */
1304 graphic_ctrl.f_w100.low_power_on=1;
1305 graphic_ctrl.f_w100.req_freq=5;
1306 break;
1307 case 1: /* 90 */
1308 case 2: /* 270 */
1309 graphic_ctrl.f_w100.req_freq=4;
1310 break;
1311 default:
1312 break;
1313 }
1314 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1315 break;
1316 }
1317 break;
1318 case CHIP_ID_W3200:
1319 case CHIP_ID_W3220:
1320 graphic_ctrl.f_w32xx.color_depth=6;
1321 graphic_ctrl.f_w32xx.en_crtc=1;
1322 graphic_ctrl.f_w32xx.en_graphic_req=1;
1323 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1324 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1325 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1326 graphic_ctrl.f_w32xx.low_power_on=0;
1327 graphic_ctrl.f_w32xx.req_freq=0;
1328 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1329 graphic_ctrl.f_w32xx.portrait_mode=rot;
1330 break;
1331 }
1332
1333 /* Set the pixel clock source and divider */
1334 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1336 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1337
Richard Purdieaac51f02005-09-06 15:19:03 -07001338 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1339 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1340 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
1342
1343
Richard Purdieaac51f02005-09-06 15:19:03 -07001344/*
1345 * Work out how long the sync pulse lasts
1346 * Value is 1/(time in seconds)
1347 */
1348static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349{
Richard Purdieaac51f02005-09-06 15:19:03 -07001350 unsigned long hsync;
1351 struct w100_mode *mode = par->mode;
1352 union crtc_ss_u crtc_ss;
1353
1354 if (mode->pixclk_src == CLK_SRC_XTAL)
1355 hsync=par->mach->xtal_freq;
1356 else
1357 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1358
1359 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1360
1361 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1362 if (crtc_ss.val)
1363 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1364 else
1365 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366}
1367
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368static void w100_suspend(u32 mode)
1369{
1370 u32 val;
1371
1372 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1373 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1374
1375 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001376 val &= ~(0x00100000); /* bit20=0 */
1377 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1379
1380 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001381 val &= ~(0x00040000); /* bit18=0 */
1382 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1384
Richard Purdieaac51f02005-09-06 15:19:03 -07001385 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
1387 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 /* CKE: Tri-State */
1389 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001390 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1392
1393 /* CLK: Stop */
1394 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001395 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1397 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1399 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1400 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1401
1402 udelay(5);
1403
1404 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001405 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 writel(val, remapped_regs + mmPLL_CNTL);
1407 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1408 }
1409}
1410
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411static void w100_vsync(void)
1412{
1413 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001414 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
1416 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1417
1418 /* set vline pos */
1419 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1420
1421 /* disable vline irq */
1422 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1423
1424 tmp &= ~0x00000002;
1425 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1426
1427 /* clear vline irq status */
1428 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1429
1430 /* enable vline irq */
1431 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1432
1433 /* clear vline irq status */
1434 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1435
1436 while(timeout > 0) {
1437 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1438 break;
1439 udelay(1);
1440 timeout--;
1441 }
1442
1443 /* disable vline irq */
1444 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1445
1446 /* clear vline irq status */
1447 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1448}
1449
Russell King3ae5eae2005-11-09 22:32:44 +00001450static struct platform_driver w100fb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 .probe = w100fb_probe,
1452 .remove = w100fb_remove,
1453 .suspend = w100fb_suspend,
1454 .resume = w100fb_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001455 .driver = {
1456 .name = "w100fb",
1457 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458};
1459
1460int __devinit w100fb_init(void)
1461{
Russell King3ae5eae2005-11-09 22:32:44 +00001462 return platform_driver_register(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463}
1464
1465void __exit w100fb_cleanup(void)
1466{
Russell King3ae5eae2005-11-09 22:32:44 +00001467 platform_driver_unregister(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468}
1469
1470module_init(w100fb_init);
1471module_exit(w100fb_cleanup);
1472
1473MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001474MODULE_LICENSE("GPL");