blob: 30469bf906e54b51a6688918624fd030ab6f25cf [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.
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -08007 * Copyright (C) 2004-2006 Richard Purdie
Richard Purdieaac51f02005-09-06 15:19:03 -07008 * Copyright (c) 2005 Ian Molton
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -08009 * Copyright (c) 2006 Alberto Mardegan
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
11 * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
12 *
Richard Purdieaac51f02005-09-06 15:19:03 -070013 * Generic platform support by Ian Molton <spyro@f2s.com>
14 * and Richard Purdie <rpurdie@rpsys.net>
15 *
16 * w32xx support by Ian Molton
17 *
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -080018 * Hardware acceleration support by Alberto Mardegan
19 * <mardy@users.sourceforge.net>
20 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License version 2 as
23 * published by the Free Software Foundation.
24 *
25 */
26
27#include <linux/delay.h>
28#include <linux/fb.h>
29#include <linux/init.h>
30#include <linux/kernel.h>
31#include <linux/mm.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010032#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/string.h>
Richard Purdieaac51f02005-09-06 15:19:03 -070034#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/io.h>
36#include <asm/uaccess.h>
37#include <video/w100fb.h>
38#include "w100fb.h"
39
40/*
41 * Prototypes
42 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070043static void w100_suspend(u32 mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044static void w100_vsync(void);
Richard Purdieaac51f02005-09-06 15:19:03 -070045static void w100_hw_init(struct w100fb_par*);
46static void w100_pwm_setup(struct w100fb_par*);
47static void w100_init_clocks(struct w100fb_par*);
48static void w100_setup_memory(struct w100fb_par*);
49static void w100_init_lcd(struct w100fb_par*);
50static void w100_set_dispregs(struct w100fb_par*);
51static void w100_update_enable(void);
52static void w100_update_disable(void);
53static void calc_hsync(struct w100fb_par *par);
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -080054static void w100_init_graphic_engine(struct w100fb_par *par);
Richard Purdieaac51f02005-09-06 15:19:03 -070055struct w100_pll_info *w100_get_xtal_table(unsigned int freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57/* Pseudo palette size */
58#define MAX_PALETTES 16
59
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define W100_SUSPEND_EXTMEM 0
61#define W100_SUSPEND_ALL 1
62
Richard Purdieaac51f02005-09-06 15:19:03 -070063#define BITS_PER_PIXEL 16
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
66static void *remapped_base;
67static void *remapped_regs;
68static void *remapped_fbuf;
69
Richard Purdieaac51f02005-09-06 15:19:03 -070070#define REMAPPED_FB_LEN 0x15ffff
71
72/* This is the offset in the w100's address space we map the current
73 framebuffer memory to. We use the position of external memory as
74 we can remap internal memory to there if external isn't present. */
75#define W100_FB_BASE MEM_EXT_BASE_VALUE
76
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78/*
79 * Sysfs functions
80 */
Richard Purdieaac51f02005-09-06 15:19:03 -070081static ssize_t flip_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 struct fb_info *info = dev_get_drvdata(dev);
84 struct w100fb_par *par=info->par;
85
Richard Purdieaac51f02005-09-06 15:19:03 -070086 return sprintf(buf, "%d\n",par->flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087}
88
Richard Purdieaac51f02005-09-06 15:19:03 -070089static ssize_t flip_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
Richard Purdieaac51f02005-09-06 15:19:03 -070091 unsigned int flip;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 struct fb_info *info = dev_get_drvdata(dev);
93 struct w100fb_par *par=info->par;
94
Richard Purdieaac51f02005-09-06 15:19:03 -070095 flip = simple_strtoul(buf, NULL, 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
Richard Purdieaac51f02005-09-06 15:19:03 -070097 if (flip > 0)
98 par->flip = 1;
99 else
100 par->flip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Richard Purdieaac51f02005-09-06 15:19:03 -0700102 w100_update_disable();
103 w100_set_dispregs(par);
104 w100_update_enable();
105
106 calc_hsync(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108 return count;
109}
110
Richard Purdieaac51f02005-09-06 15:19:03 -0700111static DEVICE_ATTR(flip, 0644, flip_show, flip_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Yani Ioannou060b8842005-05-17 06:44:04 -0400113static 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 -0700114{
Richard Purdieaac51f02005-09-06 15:19:03 -0700115 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 regs = simple_strtoul(buf, NULL, 16);
117 param = readl(remapped_regs + regs);
118 printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
119 return count;
120}
121
122static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
123
Yani Ioannou060b8842005-05-17 06:44:04 -0400124static 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 -0700125{
Richard Purdieaac51f02005-09-06 15:19:03 -0700126 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 sscanf(buf, "%lx %lx", &regs, &param);
128
129 if (regs <= 0x2000) {
130 printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
131 writel(param, remapped_regs + regs);
132 }
133
134 return count;
135}
136
137static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
138
139
Richard Purdieaac51f02005-09-06 15:19:03 -0700140static ssize_t fastpllclk_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
142 struct fb_info *info = dev_get_drvdata(dev);
143 struct w100fb_par *par=info->par;
144
Richard Purdieaac51f02005-09-06 15:19:03 -0700145 return sprintf(buf, "%d\n",par->fastpll_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
Richard Purdieaac51f02005-09-06 15:19:03 -0700148static ssize_t fastpllclk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 struct fb_info *info = dev_get_drvdata(dev);
151 struct w100fb_par *par=info->par;
152
Richard Purdieaac51f02005-09-06 15:19:03 -0700153 if (simple_strtoul(buf, NULL, 10) > 0) {
154 par->fastpll_mode=1;
155 printk("w100fb: Using fast system clock (if possible)\n");
156 } else {
157 par->fastpll_mode=0;
158 printk("w100fb: Using normal system clock\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700160
161 w100_init_clocks(par);
162 calc_hsync(par);
163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 return count;
165}
166
Richard Purdieaac51f02005-09-06 15:19:03 -0700167static DEVICE_ATTR(fastpllclk, 0644, fastpllclk_show, fastpllclk_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700170 * Some touchscreens need hsync information from the video driver to
171 * function correctly. We export it here.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700173unsigned long w100fb_get_hsynclen(struct device *dev)
174{
175 struct fb_info *info = dev_get_drvdata(dev);
176 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Richard Purdieaac51f02005-09-06 15:19:03 -0700178 /* If display is blanked/suspended, hsync isn't active */
179 if (par->blanked)
180 return 0;
181 else
182 return par->hsync_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
Richard Purdieaac51f02005-09-06 15:19:03 -0700184EXPORT_SYMBOL(w100fb_get_hsynclen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Richard Purdieaac51f02005-09-06 15:19:03 -0700186static void w100fb_clear_screen(struct w100fb_par *par)
187{
188 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 -0700189}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191
192/*
193 * Set a palette value from rgb components
194 */
195static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
196 u_int trans, struct fb_info *info)
197{
198 unsigned int val;
199 int ret = 1;
200
201 /*
202 * If greyscale is true, then we convert the RGB value
203 * to greyscale no matter what visual we are using.
204 */
205 if (info->var.grayscale)
206 red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
207
208 /*
209 * 16-bit True Colour. We encode the RGB value
210 * according to the RGB bitfield information.
211 */
212 if (regno < MAX_PALETTES) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 u32 *pal = info->pseudo_palette;
214
215 val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
216 pal[regno] = val;
217 ret = 0;
218 }
219 return ret;
220}
221
222
223/*
224 * Blank the display based on value in blank_mode
225 */
226static int w100fb_blank(int blank_mode, struct fb_info *info)
227{
Richard Purdieaac51f02005-09-06 15:19:03 -0700228 struct w100fb_par *par = info->par;
229 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
231 switch(blank_mode) {
232
Richard Purdieaac51f02005-09-06 15:19:03 -0700233 case FB_BLANK_NORMAL: /* Normal blanking */
234 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
235 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
236 case FB_BLANK_POWERDOWN: /* Poweroff */
237 if (par->blanked == 0) {
238 if(tg && tg->suspend)
239 tg->suspend(par);
240 par->blanked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 }
242 break;
243
244 case FB_BLANK_UNBLANK: /* Unblanking */
Richard Purdieaac51f02005-09-06 15:19:03 -0700245 if (par->blanked != 0) {
246 if(tg && tg->resume)
247 tg->resume(par);
248 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 }
250 break;
251 }
252 return 0;
253}
254
Richard Purdieaac51f02005-09-06 15:19:03 -0700255
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800256static void w100_fifo_wait(int entries)
257{
258 union rbbm_status_u status;
259 int i;
260
261 for (i = 0; i < 2000000; i++) {
262 status.val = readl(remapped_regs + mmRBBM_STATUS);
263 if (status.f.cmdfifo_avail >= entries)
264 return;
265 udelay(1);
266 }
267 printk(KERN_ERR "w100fb: FIFO Timeout!\n");
268}
269
270
271static int w100fb_sync(struct fb_info *info)
272{
273 union rbbm_status_u status;
274 int i;
275
276 for (i = 0; i < 2000000; i++) {
277 status.val = readl(remapped_regs + mmRBBM_STATUS);
278 if (!status.f.gui_active)
279 return 0;
280 udelay(1);
281 }
282 printk(KERN_ERR "w100fb: Graphic engine timeout!\n");
283 return -EBUSY;
284}
285
286
287static void w100_init_graphic_engine(struct w100fb_par *par)
288{
289 union dp_gui_master_cntl_u gmc;
290 union dp_mix_u dp_mix;
291 union dp_datatype_u dp_datatype;
292 union dp_cntl_u dp_cntl;
293
294 w100_fifo_wait(4);
295 writel(W100_FB_BASE, remapped_regs + mmDST_OFFSET);
296 writel(par->xres, remapped_regs + mmDST_PITCH);
297 writel(W100_FB_BASE, remapped_regs + mmSRC_OFFSET);
298 writel(par->xres, remapped_regs + mmSRC_PITCH);
299
300 w100_fifo_wait(3);
301 writel(0, remapped_regs + mmSC_TOP_LEFT);
302 writel((par->yres << 16) | par->xres, remapped_regs + mmSC_BOTTOM_RIGHT);
303 writel(0x1fff1fff, remapped_regs + mmSRC_SC_BOTTOM_RIGHT);
304
305 w100_fifo_wait(4);
306 dp_cntl.val = 0;
307 dp_cntl.f.dst_x_dir = 1;
308 dp_cntl.f.dst_y_dir = 1;
309 dp_cntl.f.src_x_dir = 1;
310 dp_cntl.f.src_y_dir = 1;
311 dp_cntl.f.dst_major_x = 1;
312 dp_cntl.f.src_major_x = 1;
313 writel(dp_cntl.val, remapped_regs + mmDP_CNTL);
314
315 gmc.val = 0;
316 gmc.f.gmc_src_pitch_offset_cntl = 1;
317 gmc.f.gmc_dst_pitch_offset_cntl = 1;
318 gmc.f.gmc_src_clipping = 1;
319 gmc.f.gmc_dst_clipping = 1;
320 gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
321 gmc.f.gmc_dst_datatype = 3; /* from DstType_16Bpp_444 */
322 gmc.f.gmc_src_datatype = SRC_DATATYPE_EQU_DST;
323 gmc.f.gmc_byte_pix_order = 1;
324 gmc.f.gmc_default_sel = 0;
325 gmc.f.gmc_rop3 = ROP3_SRCCOPY;
326 gmc.f.gmc_dp_src_source = DP_SRC_MEM_RECTANGULAR;
327 gmc.f.gmc_clr_cmp_fcn_dis = 1;
328 gmc.f.gmc_wr_msk_dis = 1;
329 gmc.f.gmc_dp_op = DP_OP_ROP;
330 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
331
332 dp_datatype.val = dp_mix.val = 0;
333 dp_datatype.f.dp_dst_datatype = gmc.f.gmc_dst_datatype;
334 dp_datatype.f.dp_brush_datatype = gmc.f.gmc_brush_datatype;
335 dp_datatype.f.dp_src2_type = 0;
336 dp_datatype.f.dp_src2_datatype = gmc.f.gmc_src_datatype;
337 dp_datatype.f.dp_src_datatype = gmc.f.gmc_src_datatype;
338 dp_datatype.f.dp_byte_pix_order = gmc.f.gmc_byte_pix_order;
339 writel(dp_datatype.val, remapped_regs + mmDP_DATATYPE);
340
341 dp_mix.f.dp_src_source = gmc.f.gmc_dp_src_source;
342 dp_mix.f.dp_src2_source = 1;
343 dp_mix.f.dp_rop3 = gmc.f.gmc_rop3;
344 dp_mix.f.dp_op = gmc.f.gmc_dp_op;
345 writel(dp_mix.val, remapped_regs + mmDP_MIX);
346}
347
348
349static void w100fb_fillrect(struct fb_info *info,
350 const struct fb_fillrect *rect)
351{
352 union dp_gui_master_cntl_u gmc;
353
354 if (info->state != FBINFO_STATE_RUNNING)
355 return;
356 if (info->flags & FBINFO_HWACCEL_DISABLED) {
357 cfb_fillrect(info, rect);
358 return;
359 }
360
361 gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
362 gmc.f.gmc_rop3 = ROP3_PATCOPY;
363 gmc.f.gmc_brush_datatype = GMC_BRUSH_SOLID_COLOR;
364 w100_fifo_wait(2);
365 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
366 writel(rect->color, remapped_regs + mmDP_BRUSH_FRGD_CLR);
367
368 w100_fifo_wait(2);
369 writel((rect->dy << 16) | (rect->dx & 0xffff), remapped_regs + mmDST_Y_X);
370 writel((rect->width << 16) | (rect->height & 0xffff),
371 remapped_regs + mmDST_WIDTH_HEIGHT);
372}
373
374
375static void w100fb_copyarea(struct fb_info *info,
376 const struct fb_copyarea *area)
377{
378 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
379 u32 h = area->height, w = area->width;
380 union dp_gui_master_cntl_u gmc;
381
382 if (info->state != FBINFO_STATE_RUNNING)
383 return;
384 if (info->flags & FBINFO_HWACCEL_DISABLED) {
385 cfb_copyarea(info, area);
386 return;
387 }
388
389 gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
390 gmc.f.gmc_rop3 = ROP3_SRCCOPY;
391 gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
392 w100_fifo_wait(1);
393 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
394
395 w100_fifo_wait(3);
396 writel((sy << 16) | (sx & 0xffff), remapped_regs + mmSRC_Y_X);
397 writel((dy << 16) | (dx & 0xffff), remapped_regs + mmDST_Y_X);
398 writel((w << 16) | (h & 0xffff), remapped_regs + mmDST_WIDTH_HEIGHT);
399}
400
401
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402/*
403 * Change the resolution by calling the appropriate hardware functions
404 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700405static void w100fb_activate_var(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406{
Richard Purdieaac51f02005-09-06 15:19:03 -0700407 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Richard Purdieaac51f02005-09-06 15:19:03 -0700409 w100_pwm_setup(par);
410 w100_setup_memory(par);
411 w100_init_clocks(par);
412 w100fb_clear_screen(par);
413 w100_vsync();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
Richard Purdieaac51f02005-09-06 15:19:03 -0700415 w100_update_disable();
416 w100_init_lcd(par);
417 w100_set_dispregs(par);
418 w100_update_enable();
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800419 w100_init_graphic_engine(par);
Richard Purdieaac51f02005-09-06 15:19:03 -0700420
421 calc_hsync(par);
422
423 if (!par->blanked && tg && tg->change)
424 tg->change(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425}
426
Richard Purdieaac51f02005-09-06 15:19:03 -0700427
428/* Select the smallest mode that allows the desired resolution to be
429 * displayed. If desired, the x and y parameters can be rounded up to
430 * match the selected mode.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700432static 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 -0700433{
Richard Purdieaac51f02005-09-06 15:19:03 -0700434 struct w100_mode *mode = NULL;
435 struct w100_mode *modelist = par->mach->modelist;
436 unsigned int best_x = 0xffffffff, best_y = 0xffffffff;
437 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Richard Purdieaac51f02005-09-06 15:19:03 -0700439 for (i = 0 ; i < par->mach->num_modes ; i++) {
440 if (modelist[i].xres >= *x && modelist[i].yres >= *y &&
441 modelist[i].xres < best_x && modelist[i].yres < best_y) {
442 best_x = modelist[i].xres;
443 best_y = modelist[i].yres;
444 mode = &modelist[i];
445 } else if(modelist[i].xres >= *y && modelist[i].yres >= *x &&
446 modelist[i].xres < best_y && modelist[i].yres < best_x) {
447 best_x = modelist[i].yres;
448 best_y = modelist[i].xres;
449 mode = &modelist[i];
450 }
451 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Richard Purdieaac51f02005-09-06 15:19:03 -0700453 if (mode && saveval) {
454 *x = best_x;
455 *y = best_y;
456 }
457
458 return mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
461
462/*
463 * w100fb_check_var():
464 * Get the video params out of 'var'. If a value doesn't fit, round it up,
465 * if it's too big, return -EINVAL.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 */
467static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
468{
Richard Purdieaac51f02005-09-06 15:19:03 -0700469 struct w100fb_par *par=info->par;
470
471 if(!w100fb_get_mode(par, &var->xres, &var->yres, 1))
472 return -EINVAL;
473
474 if (par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (par->mach->mem->size+1)))
475 return -EINVAL;
476
477 if (!par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)))
478 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 var->xres_virtual = max(var->xres_virtual, var->xres);
481 var->yres_virtual = max(var->yres_virtual, var->yres);
482
483 if (var->bits_per_pixel > BITS_PER_PIXEL)
484 return -EINVAL;
485 else
486 var->bits_per_pixel = BITS_PER_PIXEL;
487
488 var->red.offset = 11;
489 var->red.length = 5;
490 var->green.offset = 5;
491 var->green.length = 6;
492 var->blue.offset = 0;
493 var->blue.length = 5;
494 var->transp.offset = var->transp.length = 0;
495
496 var->nonstd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 var->height = -1;
498 var->width = -1;
499 var->vmode = FB_VMODE_NONINTERLACED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 var->sync = 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700501 var->pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 return 0;
504}
505
506
507/*
508 * w100fb_set_par():
509 * Set the user defined part of the display for the specified console
510 * by looking at the values in info.var
511 */
512static int w100fb_set_par(struct fb_info *info)
513{
514 struct w100fb_par *par=info->par;
515
Richard Purdieaac51f02005-09-06 15:19:03 -0700516 if (par->xres != info->var.xres || par->yres != info->var.yres) {
517 par->xres = info->var.xres;
518 par->yres = info->var.yres;
519 par->mode = w100fb_get_mode(par, &par->xres, &par->yres, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Richard Purdieaac51f02005-09-06 15:19:03 -0700521 info->fix.visual = FB_VISUAL_TRUECOLOR;
522 info->fix.ypanstep = 0;
523 info->fix.ywrapstep = 0;
524 info->fix.line_length = par->xres * BITS_PER_PIXEL / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Richard Purdieaac51f02005-09-06 15:19:03 -0700526 if ((par->xres*par->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)) {
527 par->extmem_active = 1;
528 info->fix.smem_len = par->mach->mem->size+1;
529 } else {
530 par->extmem_active = 0;
531 info->fix.smem_len = MEM_INT_SIZE+1;
532 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Richard Purdieaac51f02005-09-06 15:19:03 -0700534 w100fb_activate_var(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 return 0;
537}
538
539
540/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700541 * Frame buffer operations
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 */
543static struct fb_ops w100fb_ops = {
Richard Purdieaac51f02005-09-06 15:19:03 -0700544 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 .fb_check_var = w100fb_check_var,
Richard Purdieaac51f02005-09-06 15:19:03 -0700546 .fb_set_par = w100fb_set_par,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 .fb_setcolreg = w100fb_setcolreg,
Richard Purdieaac51f02005-09-06 15:19:03 -0700548 .fb_blank = w100fb_blank,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800549 .fb_fillrect = w100fb_fillrect,
550 .fb_copyarea = w100fb_copyarea,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 .fb_imageblit = cfb_imageblit,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800552 .fb_sync = w100fb_sync,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553};
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555#ifdef CONFIG_PM
Richard Purdieaac51f02005-09-06 15:19:03 -0700556static void w100fb_save_vidmem(struct w100fb_par *par)
557{
558 int memsize;
559
560 if (par->extmem_active) {
561 memsize=par->mach->mem->size;
562 par->saved_extmem = vmalloc(memsize);
563 if (par->saved_extmem)
564 memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
565 }
566 memsize=MEM_INT_SIZE;
567 par->saved_intmem = vmalloc(memsize);
568 if (par->saved_intmem && par->extmem_active)
569 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
570 else if (par->saved_intmem)
571 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
572}
573
574static void w100fb_restore_vidmem(struct w100fb_par *par)
575{
576 int memsize;
577
578 if (par->extmem_active && par->saved_extmem) {
579 memsize=par->mach->mem->size;
580 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
581 vfree(par->saved_extmem);
582 }
583 if (par->saved_intmem) {
584 memsize=MEM_INT_SIZE;
585 if (par->extmem_active)
586 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
587 else
588 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
589 vfree(par->saved_intmem);
590 }
591}
592
Russell King3ae5eae2005-11-09 22:32:44 +0000593static int w100fb_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Russell King3ae5eae2005-11-09 22:32:44 +0000595 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700596 struct w100fb_par *par=info->par;
597 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Russell King9480e302005-10-28 09:52:56 -0700599 w100fb_save_vidmem(par);
600 if(tg && tg->suspend)
601 tg->suspend(par);
602 w100_suspend(W100_SUSPEND_ALL);
603 par->blanked = 1;
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 return 0;
606}
607
Russell King3ae5eae2005-11-09 22:32:44 +0000608static int w100fb_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
Russell King3ae5eae2005-11-09 22:32:44 +0000610 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700611 struct w100fb_par *par=info->par;
612 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Russell King9480e302005-10-28 09:52:56 -0700614 w100_hw_init(par);
615 w100fb_activate_var(par);
616 w100fb_restore_vidmem(par);
617 if(tg && tg->resume)
618 tg->resume(par);
619 par->blanked = 0;
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return 0;
622}
623#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700624#define w100fb_suspend NULL
625#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626#endif
627
628
Russell King3ae5eae2005-11-09 22:32:44 +0000629int __init w100fb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
Richard Purdieaac51f02005-09-06 15:19:03 -0700631 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700633 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 struct w100fb_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700636 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 if (!mem)
639 return -EINVAL;
640
Richard Purdieaac51f02005-09-06 15:19:03 -0700641 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
643 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700644 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Richard Purdieaac51f02005-09-06 15:19:03 -0700646 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700648 if (remapped_regs == NULL)
649 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Richard Purdieaac51f02005-09-06 15:19:03 -0700651 /* Identify the chip */
652 printk("Found ");
653 chip_id = readl(remapped_regs + mmCHIP_ID);
654 switch(chip_id) {
655 case CHIP_ID_W100: printk("w100"); break;
656 case CHIP_ID_W3200: printk("w3200"); break;
657 case CHIP_ID_W3220: printk("w3220"); break;
658 default:
659 printk("Unknown imageon chip ID\n");
660 err = -ENODEV;
661 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700663 printk(" at 0x%08lx.\n", (unsigned long) mem->start+W100_CFG_BASE);
Richard Purdieaac51f02005-09-06 15:19:03 -0700664
665 /* Remap the framebuffer */
666 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
667 if (remapped_fbuf == NULL)
668 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Richard Purdieafdd3b32005-11-13 16:06:38 -0800670 info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700672 err = -ENOMEM;
673 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 }
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 par = info->par;
Russell King3ae5eae2005-11-09 22:32:44 +0000677 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Russell King3ae5eae2005-11-09 22:32:44 +0000679 inf = pdev->dev.platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700680 par->chip_id = chip_id;
681 par->mach = inf;
682 par->fastpll_mode = 0;
683 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Richard Purdieaac51f02005-09-06 15:19:03 -0700685 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
686 if (!par->pll_table) {
687 printk(KERN_ERR "No matching Xtal definition found\n");
688 err = -EINVAL;
689 goto out;
690 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
692 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
693 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700694 err = -ENOMEM;
695 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 }
697
698 info->fbops = &w100fb_ops;
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800699 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
700 FBINFO_HWACCEL_FILLRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700702 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 info->screen_size = REMAPPED_FB_LEN;
704
Richard Purdieaac51f02005-09-06 15:19:03 -0700705 strcpy(info->fix.id, "w100fb");
706 info->fix.type = FB_TYPE_PACKED_PIXELS;
707 info->fix.type_aux = 0;
708 info->fix.accel = FB_ACCEL_NONE;
709 info->fix.smem_start = mem->start+W100_FB_BASE;
710 info->fix.mmio_start = mem->start+W100_REG_BASE;
711 info->fix.mmio_len = W100_REG_LEN;
712
713 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
714 err = -ENOMEM;
715 goto out;
716 }
717
718 par->mode = &inf->modelist[0];
719 if(inf->init_mode & INIT_MODE_ROTATED) {
720 info->var.xres = par->mode->yres;
721 info->var.yres = par->mode->xres;
722 }
723 else {
724 info->var.xres = par->mode->xres;
725 info->var.yres = par->mode->yres;
726 }
727
728 if(inf->init_mode &= INIT_MODE_FLIPPED)
729 par->flip = 1;
730 else
731 par->flip = 0;
732
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700735 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 info->var.sync = 0;
737 info->var.grayscale = 0;
738 info->var.xoffset = info->var.yoffset = 0;
739 info->var.accel_flags = 0;
740 info->var.activate = FB_ACTIVATE_NOW;
741
Richard Purdieaac51f02005-09-06 15:19:03 -0700742 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Richard Purdieaac51f02005-09-06 15:19:03 -0700744 if (w100fb_check_var(&info->var, info) < 0) {
745 err = -EINVAL;
746 goto out;
747 }
748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 w100fb_set_par(info);
750
751 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700752 err = -EINVAL;
753 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
755
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700756 err = device_create_file(&pdev->dev, &dev_attr_fastpllclk);
757 err |= device_create_file(&pdev->dev, &dev_attr_reg_read);
758 err |= device_create_file(&pdev->dev, &dev_attr_reg_write);
759 err |= device_create_file(&pdev->dev, &dev_attr_flip);
760
761 if (err != 0)
762 printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
763 info->node, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
766 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700767out:
Andrew Morton8224c3b2008-04-28 02:15:45 -0700768 if (info) {
769 fb_dealloc_cmap(&info->cmap);
770 kfree(info->pseudo_palette);
771 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700772 if (remapped_fbuf != NULL)
773 iounmap(remapped_fbuf);
774 if (remapped_regs != NULL)
775 iounmap(remapped_regs);
776 if (remapped_base != NULL)
777 iounmap(remapped_base);
778 if (info)
779 framebuffer_release(info);
780 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781}
782
783
Russell King3ae5eae2005-11-09 22:32:44 +0000784static int w100fb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
Russell King3ae5eae2005-11-09 22:32:44 +0000786 struct fb_info *info = platform_get_drvdata(pdev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700787 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Russell King3ae5eae2005-11-09 22:32:44 +0000789 device_remove_file(&pdev->dev, &dev_attr_fastpllclk);
790 device_remove_file(&pdev->dev, &dev_attr_reg_read);
791 device_remove_file(&pdev->dev, &dev_attr_reg_write);
792 device_remove_file(&pdev->dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794 unregister_framebuffer(info);
795
Richard Purdieaac51f02005-09-06 15:19:03 -0700796 vfree(par->saved_intmem);
797 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700799 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 iounmap(remapped_base);
802 iounmap(remapped_regs);
803 iounmap(remapped_fbuf);
804
805 framebuffer_release(info);
806
807 return 0;
808}
809
810
811/* ------------------- chipset specific functions -------------------------- */
812
813
814static void w100_soft_reset(void)
815{
816 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
817 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
818 udelay(100);
819 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
820 udelay(100);
821}
822
Richard Purdieaac51f02005-09-06 15:19:03 -0700823static void w100_update_disable(void)
824{
825 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
826
827 /* Prevent display updates */
828 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
829 disp_db_buf_wr_cntl.f.update_db_buf = 0;
830 disp_db_buf_wr_cntl.f.en_db_buf = 0;
831 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
832}
833
834static void w100_update_enable(void)
835{
836 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
837
838 /* Enable display updates */
839 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
840 disp_db_buf_wr_cntl.f.update_db_buf = 1;
841 disp_db_buf_wr_cntl.f.en_db_buf = 1;
842 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
843}
844
845unsigned long w100fb_gpio_read(int port)
846{
847 unsigned long value;
848
849 if (port==W100_GPIO_PORT_A)
850 value = readl(remapped_regs + mmGPIO_DATA);
851 else
852 value = readl(remapped_regs + mmGPIO_DATA2);
853
854 return value;
855}
856
857void w100fb_gpio_write(int port, unsigned long value)
858{
859 if (port==W100_GPIO_PORT_A)
860 value = writel(value, remapped_regs + mmGPIO_DATA);
861 else
862 value = writel(value, remapped_regs + mmGPIO_DATA2);
863}
864EXPORT_SYMBOL(w100fb_gpio_read);
865EXPORT_SYMBOL(w100fb_gpio_write);
866
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867/*
868 * Initialization of critical w100 hardware
869 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700870static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871{
872 u32 temp32;
873 union cif_cntl_u cif_cntl;
874 union intf_cntl_u intf_cntl;
875 union cfgreg_base_u cfgreg_base;
876 union wrap_top_dir_u wrap_top_dir;
877 union cif_read_dbg_u cif_read_dbg;
878 union cpu_defaults_u cpu_default;
879 union cif_write_dbg_u cif_write_dbg;
880 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700882 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 w100_soft_reset();
885
886 /* This is what the fpga_init code does on reset. May be wrong
887 but there is little info available */
888 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
889 for (temp32 = 0; temp32 < 10000; temp32++)
890 readl(remapped_regs + mmSCRATCH_UMSK);
891 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
892
893 /* Set up CIF */
894 cif_io.val = defCIF_IO;
895 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
896
897 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
898 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
899 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
900 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
901 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
902
903 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
904 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
905 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
906
907 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
908 cif_cntl.f.dis_system_bits = 1;
909 cif_cntl.f.dis_mr = 1;
910 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
911 cif_cntl.f.intb_oe = 1;
912 cif_cntl.f.interrupt_active_high = 1;
913 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
914
915 /* Setup cfgINTF_CNTL and cfgCPU defaults */
916 intf_cntl.val = defINTF_CNTL;
917 intf_cntl.f.ad_inc_a = 1;
918 intf_cntl.f.ad_inc_b = 1;
919 intf_cntl.f.rd_data_rdy_a = 0;
920 intf_cntl.f.rd_data_rdy_b = 0;
921 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
922
923 cpu_default.val = defCPU_DEFAULTS;
924 cpu_default.f.access_ind_addr_a = 1;
925 cpu_default.f.access_ind_addr_b = 1;
926 cpu_default.f.access_scratch_reg = 1;
927 cpu_default.f.transition_size = 0;
928 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
929
930 /* set up the apertures */
931 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
932
933 cfgreg_base.val = defCFGREG_BASE;
934 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
935 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
936
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 wrap_start_dir.val = defWRAP_START_DIR;
938 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
939 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
940
941 wrap_top_dir.val = defWRAP_TOP_DIR;
942 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
943 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
944
945 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700946
947 /* Set the hardware to 565 colour */
948 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
949 temp32 &= 0xff7fffff;
950 temp32 |= 0x00800000;
951 writel(temp32, remapped_regs + mmDISP_DEBUG2);
952
953 /* Initialise the GPIO lines */
954 if (gpio) {
955 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
956 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
957 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
958 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
959 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
960 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962}
963
964
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965struct power_state {
966 union clk_pin_cntl_u clk_pin_cntl;
967 union pll_ref_fb_div_u pll_ref_fb_div;
968 union pll_cntl_u pll_cntl;
969 union sclk_cntl_u sclk_cntl;
970 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700972 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973};
974
975
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976static struct power_state w100_pwr_state;
977
Richard Purdieaac51f02005-09-06 15:19:03 -0700978/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
979
980/* 12.5MHz Crystal PLL Table */
981static struct w100_pll_info xtal_12500000[] = {
982 /*freq M N_int N_fac tfgoal lock_time */
983 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
984 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
985 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
986 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
987 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
988 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989};
990
Richard Purdieaac51f02005-09-06 15:19:03 -0700991/* 14.318MHz Crystal PLL Table */
992static struct w100_pll_info xtal_14318000[] = {
993 /*freq M N_int N_fac tfgoal lock_time */
994 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
995 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
996 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
997 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
998 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
999 { 0, 0, 0, 0, 0, 0},
1000};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
Richard Purdieaac51f02005-09-06 15:19:03 -07001002/* 16MHz Crystal PLL Table */
1003static struct w100_pll_info xtal_16000000[] = {
1004 /*freq M N_int N_fac tfgoal lock_time */
1005 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
1006 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
1007 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
1008 { 0, 0, 0, 0, 0, 0},
1009};
1010
1011static struct pll_entries {
1012 int xtal_freq;
1013 struct w100_pll_info *pll_table;
1014} w100_pll_tables[] = {
1015 { 12500000, &xtal_12500000[0] },
1016 { 14318000, &xtal_14318000[0] },
1017 { 16000000, &xtal_16000000[0] },
1018 { 0 },
1019};
1020
1021struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
Richard Purdieaac51f02005-09-06 15:19:03 -07001023 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
Richard Purdieaac51f02005-09-06 15:19:03 -07001025 do {
1026 if (freq == pll_entry->xtal_freq)
1027 return pll_entry->pll_table;
1028 pll_entry++;
1029 } while (pll_entry->xtal_freq);
1030 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031}
1032
1033
Richard Purdieaac51f02005-09-06 15:19:03 -07001034static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035{
Richard Purdieaac51f02005-09-06 15:19:03 -07001036 union clk_test_cntl_u clk_test_cntl;
1037
1038 udelay(5);
1039
1040 /* Select the test clock source and reset */
1041 clk_test_cntl.f.start_check_freq = 0x0;
1042 clk_test_cntl.f.testclk_sel = testclk_sel;
1043 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
1044 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1045
1046 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
1047 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1048
1049 /* Run clock test */
1050 clk_test_cntl.f.start_check_freq = 0x1;
1051 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1052
1053 /* Give the test time to complete */
1054 udelay(20);
1055
1056 /* Return the result */
1057 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
1058 clk_test_cntl.f.start_check_freq = 0x0;
1059 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1060
1061 return clk_test_cntl.f.test_count;
1062}
1063
1064
1065static int w100_pll_adjust(struct w100_pll_info *pll)
1066{
1067 unsigned int tf80;
1068 unsigned int tf20;
1069
1070 /* Initial Settings */
1071 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
1072 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
1073 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
1074 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
1075 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
1076 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
1077 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1078
1079 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
1080 * therefore, commented out the following lines
1081 * tf80 meant tf100
1082 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 do {
Richard Purdieaac51f02005-09-06 15:19:03 -07001084 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
1086 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1087
Richard Purdieaac51f02005-09-06 15:19:03 -07001088 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
1089 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 /* set VCO input = 0.2 * VDD */
1091 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
1092 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1093
Richard Purdieaac51f02005-09-06 15:19:03 -07001094 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
1095 if (tf20 <= (pll->tfgoal))
1096 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -07001099 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
1100 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 /* slow VCO config */
1102 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
1103 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1104 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 continue;
1106 }
1107 }
1108 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
1109 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001110 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1112 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001113 } else {
1114 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 } while(1);
1117}
1118
1119
1120/*
1121 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 */
Richard Purdieaac51f02005-09-06 15:19:03 -07001123static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124{
Richard Purdieaac51f02005-09-06 15:19:03 -07001125 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
Richard Purdieaac51f02005-09-06 15:19:03 -07001127 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 /* set VCO input = 0.5 * VDD */
1131 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
1132 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1133
Richard Purdieaac51f02005-09-06 15:19:03 -07001134 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135
1136 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -07001137 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1139
Richard Purdieaac51f02005-09-06 15:19:03 -07001140 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
1142 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1143
Richard Purdieaac51f02005-09-06 15:19:03 -07001144 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 /* PLL locked */
1147
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 return status;
1149}
1150
1151
Richard Purdieaac51f02005-09-06 15:19:03 -07001152static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153{
Richard Purdieaac51f02005-09-06 15:19:03 -07001154 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
Richard Purdieaac51f02005-09-06 15:19:03 -07001156 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001158 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
1159 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1161 }
1162
Richard Purdieaac51f02005-09-06 15:19:03 -07001163 /* Set system clock source to XTAL whilst adjusting the PLL! */
1164 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1166
Richard Purdieaac51f02005-09-06 15:19:03 -07001167 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1168 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1169 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1170 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1172
1173 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1174 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1175
Richard Purdieaac51f02005-09-06 15:19:03 -07001176 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
Richard Purdieaac51f02005-09-06 15:19:03 -07001178 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001180 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1181 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1183 }
1184 return status;
1185}
1186
Richard Purdieaac51f02005-09-06 15:19:03 -07001187/* freq = target frequency of the PLL */
1188static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189{
Richard Purdieaac51f02005-09-06 15:19:03 -07001190 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
Richard Purdieaac51f02005-09-06 15:19:03 -07001192 do {
1193 if (freq == pll->freq) {
1194 return w100_pll_set_clk(pll);
1195 }
1196 pll++;
1197 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 return 0;
1199}
1200
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201/* Set up an initial state. Some values/fields set
1202 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001203static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204{
1205 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1206 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1207 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1208 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001209 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1211 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1212
Richard Purdieaac51f02005-09-06 15:19:03 -07001213 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1214 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001216 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001218 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1219 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1220 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1221 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1222 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1223 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1224 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1225 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1226 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1228 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1229 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1230 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1231 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1232
Richard Purdieaac51f02005-09-06 15:19:03 -07001233 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1234 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1235 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1237
Richard Purdieaac51f02005-09-06 15:19:03 -07001238 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1239 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1241 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1242 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1243 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1244
1245 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1246 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1247 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001248 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1250 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1251 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1252 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1253 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1254 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1255 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1256 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1257 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001258 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1260 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1261 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1262 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1263 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1264
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001266 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1268 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1269 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001270 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1271 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1273 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1274 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1275
Richard Purdieaac51f02005-09-06 15:19:03 -07001276 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277}
1278
1279
Richard Purdieaac51f02005-09-06 15:19:03 -07001280/*
1281 * Setup the w100 clocks for the specified mode
1282 */
1283static void w100_init_clocks(struct w100fb_par *par)
1284{
1285 struct w100_mode *mode = par->mode;
1286
1287 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1288 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1289
1290 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1291 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1292 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1293 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1294}
1295
1296static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297{
1298 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001299 struct w100_mode *mode = par->mode;
1300 struct w100_gen_regs *regs = par->mach->regs;
1301 union active_h_disp_u active_h_disp;
1302 union active_v_disp_u active_v_disp;
1303 union graphic_h_disp_u graphic_h_disp;
1304 union graphic_v_disp_u graphic_v_disp;
1305 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Richard Purdieaac51f02005-09-06 15:19:03 -07001307 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Richard Purdieaac51f02005-09-06 15:19:03 -07001309 active_h_disp.val = 0;
1310 active_h_disp.f.active_h_start=mode->left_margin;
1311 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1312 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Richard Purdieaac51f02005-09-06 15:19:03 -07001314 active_v_disp.val = 0;
1315 active_v_disp.f.active_v_start=mode->upper_margin;
1316 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1317 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
Richard Purdieaac51f02005-09-06 15:19:03 -07001319 graphic_h_disp.val = 0;
1320 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1321 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1322 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1323
1324 graphic_v_disp.val = 0;
1325 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1326 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1327 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1328
1329 crtc_total.val = 0;
1330 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1331 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1332 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1333
1334 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1335 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1336 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1337 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1338 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1339 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1340 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1341 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1342 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1343
1344 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1345 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1346 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1347 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1348 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1349 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1350
1351 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1352 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1353 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1354 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 /* Hack for overlay in ext memory */
1357 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1358 temp32 |= 0xc0000000;
1359 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360}
1361
1362
Richard Purdieaac51f02005-09-06 15:19:03 -07001363static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364{
Richard Purdieaac51f02005-09-06 15:19:03 -07001365 union mc_ext_mem_location_u extmem_location;
1366 union mc_fb_location_u intmem_location;
1367 struct w100_mem_info *mem = par->mach->mem;
1368 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1369
1370 if (!par->extmem_active) {
1371 w100_suspend(W100_SUSPEND_EXTMEM);
1372
1373 /* Map Internal Memory at FB Base */
1374 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1375 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1376 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1377
1378 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1379 to acceleration libraries */
1380 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1381 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1382 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1383 } else {
1384 /* Map Internal Memory to its default location */
1385 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1386 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1387 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1388
1389 /* Map External Memory at FB Base */
1390 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1391 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1392 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1393
1394 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1395 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1396 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1397 udelay(100);
1398 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1399 udelay(100);
1400 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1401 udelay(100);
1402 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1403 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1404 if (bm_mem) {
1405 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1406 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1407 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1408 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1409 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1410 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1411 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1412 }
1413 }
1414}
1415
1416static void w100_set_dispregs(struct w100fb_par *par)
1417{
1418 unsigned long rot=0, divider, offset=0;
1419 union graphic_ctrl_u graphic_ctrl;
1420
1421 /* See if the mode has been rotated */
1422 if (par->xres == par->mode->xres) {
1423 if (par->flip) {
1424 rot=3; /* 180 degree */
1425 offset=(par->xres * par->yres) - 1;
1426 } /* else 0 degree */
1427 divider = par->mode->pixclk_divider;
1428 } else {
1429 if (par->flip) {
1430 rot=2; /* 270 degree */
1431 offset=par->xres - 1;
1432 } else {
1433 rot=1; /* 90 degree */
1434 offset=par->xres * (par->yres - 1);
1435 }
1436 divider = par->mode->pixclk_divider_rotated;
1437 }
1438
1439 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1440 switch (par->chip_id) {
1441 case CHIP_ID_W100:
1442 graphic_ctrl.f_w100.color_depth=6;
1443 graphic_ctrl.f_w100.en_crtc=1;
1444 graphic_ctrl.f_w100.en_graphic_req=1;
1445 graphic_ctrl.f_w100.en_graphic_crtc=1;
1446 graphic_ctrl.f_w100.lcd_pclk_on=1;
1447 graphic_ctrl.f_w100.lcd_sclk_on=1;
1448 graphic_ctrl.f_w100.low_power_on=0;
1449 graphic_ctrl.f_w100.req_freq=0;
1450 graphic_ctrl.f_w100.portrait_mode=rot;
1451
1452 /* Zaurus needs this */
1453 switch(par->xres) {
1454 case 240:
1455 case 320:
1456 default:
1457 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1458 break;
1459 case 480:
1460 case 640:
1461 switch(rot) {
1462 case 0: /* 0 */
1463 case 3: /* 180 */
1464 graphic_ctrl.f_w100.low_power_on=1;
1465 graphic_ctrl.f_w100.req_freq=5;
1466 break;
1467 case 1: /* 90 */
1468 case 2: /* 270 */
1469 graphic_ctrl.f_w100.req_freq=4;
1470 break;
1471 default:
1472 break;
1473 }
1474 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1475 break;
1476 }
1477 break;
1478 case CHIP_ID_W3200:
1479 case CHIP_ID_W3220:
1480 graphic_ctrl.f_w32xx.color_depth=6;
1481 graphic_ctrl.f_w32xx.en_crtc=1;
1482 graphic_ctrl.f_w32xx.en_graphic_req=1;
1483 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1484 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1485 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1486 graphic_ctrl.f_w32xx.low_power_on=0;
1487 graphic_ctrl.f_w32xx.req_freq=0;
1488 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1489 graphic_ctrl.f_w32xx.portrait_mode=rot;
1490 break;
1491 }
1492
1493 /* Set the pixel clock source and divider */
1494 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1496 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1497
Richard Purdieaac51f02005-09-06 15:19:03 -07001498 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1499 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1500 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501}
1502
1503
Richard Purdieaac51f02005-09-06 15:19:03 -07001504/*
1505 * Work out how long the sync pulse lasts
1506 * Value is 1/(time in seconds)
1507 */
1508static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509{
Richard Purdieaac51f02005-09-06 15:19:03 -07001510 unsigned long hsync;
1511 struct w100_mode *mode = par->mode;
1512 union crtc_ss_u crtc_ss;
1513
1514 if (mode->pixclk_src == CLK_SRC_XTAL)
1515 hsync=par->mach->xtal_freq;
1516 else
1517 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1518
1519 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1520
1521 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1522 if (crtc_ss.val)
1523 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1524 else
1525 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526}
1527
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528static void w100_suspend(u32 mode)
1529{
1530 u32 val;
1531
1532 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1533 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1534
1535 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001536 val &= ~(0x00100000); /* bit20=0 */
1537 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1539
1540 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001541 val &= ~(0x00040000); /* bit18=0 */
1542 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1544
Richard Purdieaac51f02005-09-06 15:19:03 -07001545 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 /* CKE: Tri-State */
1549 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001550 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1552
1553 /* CLK: Stop */
1554 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001555 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1557 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1559 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1560 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1561
1562 udelay(5);
1563
1564 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001565 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 writel(val, remapped_regs + mmPLL_CNTL);
1567 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1568 }
1569}
1570
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571static void w100_vsync(void)
1572{
1573 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001574 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
1576 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1577
1578 /* set vline pos */
1579 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1580
1581 /* disable vline irq */
1582 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1583
1584 tmp &= ~0x00000002;
1585 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1586
1587 /* clear vline irq status */
1588 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1589
1590 /* enable vline irq */
1591 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1592
1593 /* clear vline irq status */
1594 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1595
1596 while(timeout > 0) {
1597 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1598 break;
1599 udelay(1);
1600 timeout--;
1601 }
1602
1603 /* disable vline irq */
1604 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1605
1606 /* clear vline irq status */
1607 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1608}
1609
Russell King3ae5eae2005-11-09 22:32:44 +00001610static struct platform_driver w100fb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 .probe = w100fb_probe,
1612 .remove = w100fb_remove,
1613 .suspend = w100fb_suspend,
1614 .resume = w100fb_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001615 .driver = {
1616 .name = "w100fb",
1617 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618};
1619
1620int __devinit w100fb_init(void)
1621{
Russell King3ae5eae2005-11-09 22:32:44 +00001622 return platform_driver_register(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623}
1624
1625void __exit w100fb_cleanup(void)
1626{
Russell King3ae5eae2005-11-09 22:32:44 +00001627 platform_driver_unregister(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628}
1629
1630module_init(w100fb_init);
1631module_exit(w100fb_cleanup);
1632
1633MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001634MODULE_LICENSE("GPL");