blob: 8a141c2c637bfec54a3a364eee02e6834a0cf21a [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
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700526 mutex_lock(&info->mm_lock);
Richard Purdieaac51f02005-09-06 15:19:03 -0700527 if ((par->xres*par->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)) {
528 par->extmem_active = 1;
529 info->fix.smem_len = par->mach->mem->size+1;
530 } else {
531 par->extmem_active = 0;
532 info->fix.smem_len = MEM_INT_SIZE+1;
533 }
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700534 mutex_unlock(&info->mm_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Richard Purdieaac51f02005-09-06 15:19:03 -0700536 w100fb_activate_var(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 return 0;
539}
540
541
542/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700543 * Frame buffer operations
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 */
545static struct fb_ops w100fb_ops = {
Richard Purdieaac51f02005-09-06 15:19:03 -0700546 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 .fb_check_var = w100fb_check_var,
Richard Purdieaac51f02005-09-06 15:19:03 -0700548 .fb_set_par = w100fb_set_par,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 .fb_setcolreg = w100fb_setcolreg,
Richard Purdieaac51f02005-09-06 15:19:03 -0700550 .fb_blank = w100fb_blank,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800551 .fb_fillrect = w100fb_fillrect,
552 .fb_copyarea = w100fb_copyarea,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 .fb_imageblit = cfb_imageblit,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800554 .fb_sync = w100fb_sync,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555};
556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557#ifdef CONFIG_PM
Richard Purdieaac51f02005-09-06 15:19:03 -0700558static void w100fb_save_vidmem(struct w100fb_par *par)
559{
560 int memsize;
561
562 if (par->extmem_active) {
563 memsize=par->mach->mem->size;
564 par->saved_extmem = vmalloc(memsize);
565 if (par->saved_extmem)
566 memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
567 }
568 memsize=MEM_INT_SIZE;
569 par->saved_intmem = vmalloc(memsize);
570 if (par->saved_intmem && par->extmem_active)
571 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
572 else if (par->saved_intmem)
573 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
574}
575
576static void w100fb_restore_vidmem(struct w100fb_par *par)
577{
578 int memsize;
579
580 if (par->extmem_active && par->saved_extmem) {
581 memsize=par->mach->mem->size;
582 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
583 vfree(par->saved_extmem);
584 }
585 if (par->saved_intmem) {
586 memsize=MEM_INT_SIZE;
587 if (par->extmem_active)
588 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
589 else
590 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
591 vfree(par->saved_intmem);
592 }
593}
594
Russell King3ae5eae2005-11-09 22:32:44 +0000595static int w100fb_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
Russell King3ae5eae2005-11-09 22:32:44 +0000597 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700598 struct w100fb_par *par=info->par;
599 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Russell King9480e302005-10-28 09:52:56 -0700601 w100fb_save_vidmem(par);
602 if(tg && tg->suspend)
603 tg->suspend(par);
604 w100_suspend(W100_SUSPEND_ALL);
605 par->blanked = 1;
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 return 0;
608}
609
Russell King3ae5eae2005-11-09 22:32:44 +0000610static int w100fb_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
Russell King3ae5eae2005-11-09 22:32:44 +0000612 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700613 struct w100fb_par *par=info->par;
614 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
Russell King9480e302005-10-28 09:52:56 -0700616 w100_hw_init(par);
617 w100fb_activate_var(par);
618 w100fb_restore_vidmem(par);
619 if(tg && tg->resume)
620 tg->resume(par);
621 par->blanked = 0;
622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return 0;
624}
625#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700626#define w100fb_suspend NULL
627#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628#endif
629
630
Russell King3ae5eae2005-11-09 22:32:44 +0000631int __init w100fb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
Richard Purdieaac51f02005-09-06 15:19:03 -0700633 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700635 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 struct w100fb_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700638 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 if (!mem)
641 return -EINVAL;
642
Richard Purdieaac51f02005-09-06 15:19:03 -0700643 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
645 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700646 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647
Richard Purdieaac51f02005-09-06 15:19:03 -0700648 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700650 if (remapped_regs == NULL)
651 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Richard Purdieaac51f02005-09-06 15:19:03 -0700653 /* Identify the chip */
654 printk("Found ");
655 chip_id = readl(remapped_regs + mmCHIP_ID);
656 switch(chip_id) {
657 case CHIP_ID_W100: printk("w100"); break;
658 case CHIP_ID_W3200: printk("w3200"); break;
659 case CHIP_ID_W3220: printk("w3220"); break;
660 default:
661 printk("Unknown imageon chip ID\n");
662 err = -ENODEV;
663 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 }
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700665 printk(" at 0x%08lx.\n", (unsigned long) mem->start+W100_CFG_BASE);
Richard Purdieaac51f02005-09-06 15:19:03 -0700666
667 /* Remap the framebuffer */
668 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
669 if (remapped_fbuf == NULL)
670 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Richard Purdieafdd3b32005-11-13 16:06:38 -0800672 info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700674 err = -ENOMEM;
675 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 }
677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 par = info->par;
Russell King3ae5eae2005-11-09 22:32:44 +0000679 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Russell King3ae5eae2005-11-09 22:32:44 +0000681 inf = pdev->dev.platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700682 par->chip_id = chip_id;
683 par->mach = inf;
684 par->fastpll_mode = 0;
685 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Richard Purdieaac51f02005-09-06 15:19:03 -0700687 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
688 if (!par->pll_table) {
689 printk(KERN_ERR "No matching Xtal definition found\n");
690 err = -EINVAL;
691 goto out;
692 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
695 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700696 err = -ENOMEM;
697 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 }
699
700 info->fbops = &w100fb_ops;
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800701 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
702 FBINFO_HWACCEL_FILLRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700704 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 info->screen_size = REMAPPED_FB_LEN;
706
Richard Purdieaac51f02005-09-06 15:19:03 -0700707 strcpy(info->fix.id, "w100fb");
708 info->fix.type = FB_TYPE_PACKED_PIXELS;
709 info->fix.type_aux = 0;
710 info->fix.accel = FB_ACCEL_NONE;
711 info->fix.smem_start = mem->start+W100_FB_BASE;
712 info->fix.mmio_start = mem->start+W100_REG_BASE;
713 info->fix.mmio_len = W100_REG_LEN;
714
715 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
716 err = -ENOMEM;
717 goto out;
718 }
719
720 par->mode = &inf->modelist[0];
721 if(inf->init_mode & INIT_MODE_ROTATED) {
722 info->var.xres = par->mode->yres;
723 info->var.yres = par->mode->xres;
724 }
725 else {
726 info->var.xres = par->mode->xres;
727 info->var.yres = par->mode->yres;
728 }
729
730 if(inf->init_mode &= INIT_MODE_FLIPPED)
731 par->flip = 1;
732 else
733 par->flip = 0;
734
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700737 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 info->var.sync = 0;
739 info->var.grayscale = 0;
740 info->var.xoffset = info->var.yoffset = 0;
741 info->var.accel_flags = 0;
742 info->var.activate = FB_ACTIVATE_NOW;
743
Richard Purdieaac51f02005-09-06 15:19:03 -0700744 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Richard Purdieaac51f02005-09-06 15:19:03 -0700746 if (w100fb_check_var(&info->var, info) < 0) {
747 err = -EINVAL;
748 goto out;
749 }
750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 w100fb_set_par(info);
752
753 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700754 err = -EINVAL;
755 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 }
757
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700758 err = device_create_file(&pdev->dev, &dev_attr_fastpllclk);
759 err |= device_create_file(&pdev->dev, &dev_attr_reg_read);
760 err |= device_create_file(&pdev->dev, &dev_attr_reg_write);
761 err |= device_create_file(&pdev->dev, &dev_attr_flip);
762
763 if (err != 0)
764 printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
765 info->node, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
768 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700769out:
Andrew Morton8224c3b2008-04-28 02:15:45 -0700770 if (info) {
771 fb_dealloc_cmap(&info->cmap);
772 kfree(info->pseudo_palette);
773 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700774 if (remapped_fbuf != NULL)
775 iounmap(remapped_fbuf);
776 if (remapped_regs != NULL)
777 iounmap(remapped_regs);
778 if (remapped_base != NULL)
779 iounmap(remapped_base);
780 if (info)
781 framebuffer_release(info);
782 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783}
784
785
Russell King3ae5eae2005-11-09 22:32:44 +0000786static int w100fb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787{
Russell King3ae5eae2005-11-09 22:32:44 +0000788 struct fb_info *info = platform_get_drvdata(pdev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700789 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Russell King3ae5eae2005-11-09 22:32:44 +0000791 device_remove_file(&pdev->dev, &dev_attr_fastpllclk);
792 device_remove_file(&pdev->dev, &dev_attr_reg_read);
793 device_remove_file(&pdev->dev, &dev_attr_reg_write);
794 device_remove_file(&pdev->dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 unregister_framebuffer(info);
797
Richard Purdieaac51f02005-09-06 15:19:03 -0700798 vfree(par->saved_intmem);
799 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700801 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803 iounmap(remapped_base);
804 iounmap(remapped_regs);
805 iounmap(remapped_fbuf);
806
807 framebuffer_release(info);
808
809 return 0;
810}
811
812
813/* ------------------- chipset specific functions -------------------------- */
814
815
816static void w100_soft_reset(void)
817{
818 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
819 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
820 udelay(100);
821 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
822 udelay(100);
823}
824
Richard Purdieaac51f02005-09-06 15:19:03 -0700825static void w100_update_disable(void)
826{
827 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
828
829 /* Prevent display updates */
830 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
831 disp_db_buf_wr_cntl.f.update_db_buf = 0;
832 disp_db_buf_wr_cntl.f.en_db_buf = 0;
833 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
834}
835
836static void w100_update_enable(void)
837{
838 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
839
840 /* Enable display updates */
841 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
842 disp_db_buf_wr_cntl.f.update_db_buf = 1;
843 disp_db_buf_wr_cntl.f.en_db_buf = 1;
844 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
845}
846
847unsigned long w100fb_gpio_read(int port)
848{
849 unsigned long value;
850
851 if (port==W100_GPIO_PORT_A)
852 value = readl(remapped_regs + mmGPIO_DATA);
853 else
854 value = readl(remapped_regs + mmGPIO_DATA2);
855
856 return value;
857}
858
859void w100fb_gpio_write(int port, unsigned long value)
860{
861 if (port==W100_GPIO_PORT_A)
862 value = writel(value, remapped_regs + mmGPIO_DATA);
863 else
864 value = writel(value, remapped_regs + mmGPIO_DATA2);
865}
866EXPORT_SYMBOL(w100fb_gpio_read);
867EXPORT_SYMBOL(w100fb_gpio_write);
868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869/*
870 * Initialization of critical w100 hardware
871 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700872static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873{
874 u32 temp32;
875 union cif_cntl_u cif_cntl;
876 union intf_cntl_u intf_cntl;
877 union cfgreg_base_u cfgreg_base;
878 union wrap_top_dir_u wrap_top_dir;
879 union cif_read_dbg_u cif_read_dbg;
880 union cpu_defaults_u cpu_default;
881 union cif_write_dbg_u cif_write_dbg;
882 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700884 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
886 w100_soft_reset();
887
888 /* This is what the fpga_init code does on reset. May be wrong
889 but there is little info available */
890 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
891 for (temp32 = 0; temp32 < 10000; temp32++)
892 readl(remapped_regs + mmSCRATCH_UMSK);
893 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
894
895 /* Set up CIF */
896 cif_io.val = defCIF_IO;
897 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
898
899 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
900 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
901 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
902 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
903 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
904
905 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
906 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
907 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
908
909 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
910 cif_cntl.f.dis_system_bits = 1;
911 cif_cntl.f.dis_mr = 1;
912 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
913 cif_cntl.f.intb_oe = 1;
914 cif_cntl.f.interrupt_active_high = 1;
915 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
916
917 /* Setup cfgINTF_CNTL and cfgCPU defaults */
918 intf_cntl.val = defINTF_CNTL;
919 intf_cntl.f.ad_inc_a = 1;
920 intf_cntl.f.ad_inc_b = 1;
921 intf_cntl.f.rd_data_rdy_a = 0;
922 intf_cntl.f.rd_data_rdy_b = 0;
923 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
924
925 cpu_default.val = defCPU_DEFAULTS;
926 cpu_default.f.access_ind_addr_a = 1;
927 cpu_default.f.access_ind_addr_b = 1;
928 cpu_default.f.access_scratch_reg = 1;
929 cpu_default.f.transition_size = 0;
930 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
931
932 /* set up the apertures */
933 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
934
935 cfgreg_base.val = defCFGREG_BASE;
936 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
937 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 wrap_start_dir.val = defWRAP_START_DIR;
940 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
941 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
942
943 wrap_top_dir.val = defWRAP_TOP_DIR;
944 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
945 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
946
947 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700948
949 /* Set the hardware to 565 colour */
950 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
951 temp32 &= 0xff7fffff;
952 temp32 |= 0x00800000;
953 writel(temp32, remapped_regs + mmDISP_DEBUG2);
954
955 /* Initialise the GPIO lines */
956 if (gpio) {
957 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
958 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
959 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
960 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
961 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
962 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
963 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964}
965
966
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967struct power_state {
968 union clk_pin_cntl_u clk_pin_cntl;
969 union pll_ref_fb_div_u pll_ref_fb_div;
970 union pll_cntl_u pll_cntl;
971 union sclk_cntl_u sclk_cntl;
972 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700974 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975};
976
977
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978static struct power_state w100_pwr_state;
979
Richard Purdieaac51f02005-09-06 15:19:03 -0700980/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
981
982/* 12.5MHz Crystal PLL Table */
983static struct w100_pll_info xtal_12500000[] = {
984 /*freq M N_int N_fac tfgoal lock_time */
985 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
986 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
987 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
988 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
989 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
990 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991};
992
Richard Purdieaac51f02005-09-06 15:19:03 -0700993/* 14.318MHz Crystal PLL Table */
994static struct w100_pll_info xtal_14318000[] = {
995 /*freq M N_int N_fac tfgoal lock_time */
996 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
997 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
998 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
999 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
1000 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
1001 { 0, 0, 0, 0, 0, 0},
1002};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
Richard Purdieaac51f02005-09-06 15:19:03 -07001004/* 16MHz Crystal PLL Table */
1005static struct w100_pll_info xtal_16000000[] = {
1006 /*freq M N_int N_fac tfgoal lock_time */
1007 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
Philipp Zabel27c8d952008-07-04 09:59:53 -07001008 { 80, 1, 9, 0, 0xe0, 13}, /* tfgoal guessed */
Richard Purdieaac51f02005-09-06 15:19:03 -07001009 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
1010 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
1011 { 0, 0, 0, 0, 0, 0},
1012};
1013
1014static struct pll_entries {
1015 int xtal_freq;
1016 struct w100_pll_info *pll_table;
1017} w100_pll_tables[] = {
1018 { 12500000, &xtal_12500000[0] },
1019 { 14318000, &xtal_14318000[0] },
1020 { 16000000, &xtal_16000000[0] },
1021 { 0 },
1022};
1023
1024struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
Richard Purdieaac51f02005-09-06 15:19:03 -07001026 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
Richard Purdieaac51f02005-09-06 15:19:03 -07001028 do {
1029 if (freq == pll_entry->xtal_freq)
1030 return pll_entry->pll_table;
1031 pll_entry++;
1032 } while (pll_entry->xtal_freq);
1033 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034}
1035
1036
Richard Purdieaac51f02005-09-06 15:19:03 -07001037static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
Richard Purdieaac51f02005-09-06 15:19:03 -07001039 union clk_test_cntl_u clk_test_cntl;
1040
1041 udelay(5);
1042
1043 /* Select the test clock source and reset */
1044 clk_test_cntl.f.start_check_freq = 0x0;
1045 clk_test_cntl.f.testclk_sel = testclk_sel;
1046 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
1047 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1048
1049 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
1050 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1051
1052 /* Run clock test */
1053 clk_test_cntl.f.start_check_freq = 0x1;
1054 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1055
1056 /* Give the test time to complete */
1057 udelay(20);
1058
1059 /* Return the result */
1060 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
1061 clk_test_cntl.f.start_check_freq = 0x0;
1062 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1063
1064 return clk_test_cntl.f.test_count;
1065}
1066
1067
1068static int w100_pll_adjust(struct w100_pll_info *pll)
1069{
1070 unsigned int tf80;
1071 unsigned int tf20;
1072
1073 /* Initial Settings */
1074 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
1075 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
1076 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
1077 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
1078 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
1079 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
1080 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1081
1082 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
1083 * therefore, commented out the following lines
1084 * tf80 meant tf100
1085 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 do {
Richard Purdieaac51f02005-09-06 15:19:03 -07001087 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
1089 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1090
Richard Purdieaac51f02005-09-06 15:19:03 -07001091 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
1092 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 /* set VCO input = 0.2 * VDD */
1094 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
1095 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1096
Richard Purdieaac51f02005-09-06 15:19:03 -07001097 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
1098 if (tf20 <= (pll->tfgoal))
1099 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
1101 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -07001102 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
1103 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 /* slow VCO config */
1105 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
1106 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1107 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 continue;
1109 }
1110 }
1111 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
1112 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001113 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1115 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001116 } else {
1117 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 } while(1);
1120}
1121
1122
1123/*
1124 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 */
Richard Purdieaac51f02005-09-06 15:19:03 -07001126static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127{
Richard Purdieaac51f02005-09-06 15:19:03 -07001128 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
Richard Purdieaac51f02005-09-06 15:19:03 -07001130 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 /* set VCO input = 0.5 * VDD */
1134 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
1135 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1136
Richard Purdieaac51f02005-09-06 15:19:03 -07001137 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
1139 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -07001140 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1142
Richard Purdieaac51f02005-09-06 15:19:03 -07001143 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
1145 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1146
Richard Purdieaac51f02005-09-06 15:19:03 -07001147 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148
1149 /* PLL locked */
1150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 return status;
1152}
1153
1154
Richard Purdieaac51f02005-09-06 15:19:03 -07001155static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156{
Richard Purdieaac51f02005-09-06 15:19:03 -07001157 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Richard Purdieaac51f02005-09-06 15:19:03 -07001159 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001161 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
1162 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1164 }
1165
Richard Purdieaac51f02005-09-06 15:19:03 -07001166 /* Set system clock source to XTAL whilst adjusting the PLL! */
1167 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1169
Richard Purdieaac51f02005-09-06 15:19:03 -07001170 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1171 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1172 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1173 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1175
1176 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1177 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1178
Richard Purdieaac51f02005-09-06 15:19:03 -07001179 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180
Richard Purdieaac51f02005-09-06 15:19:03 -07001181 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001183 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1184 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1186 }
1187 return status;
1188}
1189
Richard Purdieaac51f02005-09-06 15:19:03 -07001190/* freq = target frequency of the PLL */
1191static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Richard Purdieaac51f02005-09-06 15:19:03 -07001193 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
Richard Purdieaac51f02005-09-06 15:19:03 -07001195 do {
1196 if (freq == pll->freq) {
1197 return w100_pll_set_clk(pll);
1198 }
1199 pll++;
1200 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 return 0;
1202}
1203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204/* Set up an initial state. Some values/fields set
1205 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001206static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
1208 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1209 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1210 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1211 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001212 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1214 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1215
Richard Purdieaac51f02005-09-06 15:19:03 -07001216 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1217 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001219 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001221 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1222 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1223 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1224 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1225 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1226 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1227 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1228 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1229 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1231 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1232 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1233 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1234 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1235
Richard Purdieaac51f02005-09-06 15:19:03 -07001236 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1237 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1238 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1240
Richard Purdieaac51f02005-09-06 15:19:03 -07001241 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1242 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1244 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1245 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1246 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1247
1248 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1249 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1250 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001251 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1253 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1254 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1255 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1256 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1257 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1258 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1259 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1260 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001261 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1263 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1264 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1265 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1266 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001269 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1271 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1272 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001273 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1274 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1276 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1277 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1278
Richard Purdieaac51f02005-09-06 15:19:03 -07001279 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280}
1281
1282
Richard Purdieaac51f02005-09-06 15:19:03 -07001283/*
1284 * Setup the w100 clocks for the specified mode
1285 */
1286static void w100_init_clocks(struct w100fb_par *par)
1287{
1288 struct w100_mode *mode = par->mode;
1289
1290 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1291 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1292
1293 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1294 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1295 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1296 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1297}
1298
1299static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300{
1301 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001302 struct w100_mode *mode = par->mode;
1303 struct w100_gen_regs *regs = par->mach->regs;
1304 union active_h_disp_u active_h_disp;
1305 union active_v_disp_u active_v_disp;
1306 union graphic_h_disp_u graphic_h_disp;
1307 union graphic_v_disp_u graphic_v_disp;
1308 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Richard Purdieaac51f02005-09-06 15:19:03 -07001310 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
Richard Purdieaac51f02005-09-06 15:19:03 -07001312 active_h_disp.val = 0;
1313 active_h_disp.f.active_h_start=mode->left_margin;
1314 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1315 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
Richard Purdieaac51f02005-09-06 15:19:03 -07001317 active_v_disp.val = 0;
1318 active_v_disp.f.active_v_start=mode->upper_margin;
1319 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1320 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
Richard Purdieaac51f02005-09-06 15:19:03 -07001322 graphic_h_disp.val = 0;
1323 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1324 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1325 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1326
1327 graphic_v_disp.val = 0;
1328 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1329 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1330 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1331
1332 crtc_total.val = 0;
1333 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1334 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1335 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1336
1337 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1338 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1339 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1340 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1341 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1342 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1343 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1344 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1345 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1346
1347 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1348 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1349 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1350 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1351 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1352 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1353
1354 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1355 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1356 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1357 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
1359 /* Hack for overlay in ext memory */
1360 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1361 temp32 |= 0xc0000000;
1362 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363}
1364
1365
Richard Purdieaac51f02005-09-06 15:19:03 -07001366static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367{
Richard Purdieaac51f02005-09-06 15:19:03 -07001368 union mc_ext_mem_location_u extmem_location;
1369 union mc_fb_location_u intmem_location;
1370 struct w100_mem_info *mem = par->mach->mem;
1371 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1372
1373 if (!par->extmem_active) {
1374 w100_suspend(W100_SUSPEND_EXTMEM);
1375
1376 /* Map Internal Memory at FB Base */
1377 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1378 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1379 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1380
1381 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1382 to acceleration libraries */
1383 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1384 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1385 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1386 } else {
1387 /* Map Internal Memory to its default location */
1388 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1389 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1390 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1391
1392 /* Map External Memory at FB Base */
1393 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1394 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1395 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1396
1397 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1398 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1399 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1400 udelay(100);
1401 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1402 udelay(100);
1403 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1404 udelay(100);
1405 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1406 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1407 if (bm_mem) {
1408 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1409 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1410 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1411 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1412 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1413 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1414 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1415 }
1416 }
1417}
1418
1419static void w100_set_dispregs(struct w100fb_par *par)
1420{
1421 unsigned long rot=0, divider, offset=0;
1422 union graphic_ctrl_u graphic_ctrl;
1423
1424 /* See if the mode has been rotated */
1425 if (par->xres == par->mode->xres) {
1426 if (par->flip) {
1427 rot=3; /* 180 degree */
1428 offset=(par->xres * par->yres) - 1;
1429 } /* else 0 degree */
1430 divider = par->mode->pixclk_divider;
1431 } else {
1432 if (par->flip) {
1433 rot=2; /* 270 degree */
1434 offset=par->xres - 1;
1435 } else {
1436 rot=1; /* 90 degree */
1437 offset=par->xres * (par->yres - 1);
1438 }
1439 divider = par->mode->pixclk_divider_rotated;
1440 }
1441
1442 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1443 switch (par->chip_id) {
1444 case CHIP_ID_W100:
1445 graphic_ctrl.f_w100.color_depth=6;
1446 graphic_ctrl.f_w100.en_crtc=1;
1447 graphic_ctrl.f_w100.en_graphic_req=1;
1448 graphic_ctrl.f_w100.en_graphic_crtc=1;
1449 graphic_ctrl.f_w100.lcd_pclk_on=1;
1450 graphic_ctrl.f_w100.lcd_sclk_on=1;
1451 graphic_ctrl.f_w100.low_power_on=0;
1452 graphic_ctrl.f_w100.req_freq=0;
1453 graphic_ctrl.f_w100.portrait_mode=rot;
1454
1455 /* Zaurus needs this */
1456 switch(par->xres) {
1457 case 240:
1458 case 320:
1459 default:
1460 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1461 break;
1462 case 480:
1463 case 640:
1464 switch(rot) {
1465 case 0: /* 0 */
1466 case 3: /* 180 */
1467 graphic_ctrl.f_w100.low_power_on=1;
1468 graphic_ctrl.f_w100.req_freq=5;
1469 break;
1470 case 1: /* 90 */
1471 case 2: /* 270 */
1472 graphic_ctrl.f_w100.req_freq=4;
1473 break;
1474 default:
1475 break;
1476 }
1477 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1478 break;
1479 }
1480 break;
1481 case CHIP_ID_W3200:
1482 case CHIP_ID_W3220:
1483 graphic_ctrl.f_w32xx.color_depth=6;
1484 graphic_ctrl.f_w32xx.en_crtc=1;
1485 graphic_ctrl.f_w32xx.en_graphic_req=1;
1486 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1487 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1488 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1489 graphic_ctrl.f_w32xx.low_power_on=0;
1490 graphic_ctrl.f_w32xx.req_freq=0;
1491 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1492 graphic_ctrl.f_w32xx.portrait_mode=rot;
1493 break;
1494 }
1495
1496 /* Set the pixel clock source and divider */
1497 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1499 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1500
Richard Purdieaac51f02005-09-06 15:19:03 -07001501 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1502 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1503 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504}
1505
1506
Richard Purdieaac51f02005-09-06 15:19:03 -07001507/*
1508 * Work out how long the sync pulse lasts
1509 * Value is 1/(time in seconds)
1510 */
1511static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512{
Richard Purdieaac51f02005-09-06 15:19:03 -07001513 unsigned long hsync;
1514 struct w100_mode *mode = par->mode;
1515 union crtc_ss_u crtc_ss;
1516
1517 if (mode->pixclk_src == CLK_SRC_XTAL)
1518 hsync=par->mach->xtal_freq;
1519 else
1520 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1521
1522 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1523
1524 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1525 if (crtc_ss.val)
1526 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1527 else
1528 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529}
1530
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531static void w100_suspend(u32 mode)
1532{
1533 u32 val;
1534
1535 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1536 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1537
1538 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001539 val &= ~(0x00100000); /* bit20=0 */
1540 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1542
1543 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001544 val &= ~(0x00040000); /* bit18=0 */
1545 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1547
Richard Purdieaac51f02005-09-06 15:19:03 -07001548 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
1550 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 /* CKE: Tri-State */
1552 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001553 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1555
1556 /* CLK: Stop */
1557 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001558 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1560 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1562 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1563 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1564
1565 udelay(5);
1566
1567 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001568 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 writel(val, remapped_regs + mmPLL_CNTL);
1570 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1571 }
1572}
1573
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574static void w100_vsync(void)
1575{
1576 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001577 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
1579 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1580
1581 /* set vline pos */
1582 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1583
1584 /* disable vline irq */
1585 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1586
1587 tmp &= ~0x00000002;
1588 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1589
1590 /* clear vline irq status */
1591 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1592
1593 /* enable vline irq */
1594 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1595
1596 /* clear vline irq status */
1597 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1598
1599 while(timeout > 0) {
1600 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1601 break;
1602 udelay(1);
1603 timeout--;
1604 }
1605
1606 /* disable vline irq */
1607 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1608
1609 /* clear vline irq status */
1610 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1611}
1612
Russell King3ae5eae2005-11-09 22:32:44 +00001613static struct platform_driver w100fb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 .probe = w100fb_probe,
1615 .remove = w100fb_remove,
1616 .suspend = w100fb_suspend,
1617 .resume = w100fb_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001618 .driver = {
1619 .name = "w100fb",
1620 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621};
1622
1623int __devinit w100fb_init(void)
1624{
Russell King3ae5eae2005-11-09 22:32:44 +00001625 return platform_driver_register(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626}
1627
1628void __exit w100fb_cleanup(void)
1629{
Russell King3ae5eae2005-11-09 22:32:44 +00001630 platform_driver_unregister(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631}
1632
1633module_init(w100fb_init);
1634module_exit(w100fb_cleanup);
1635
1636MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001637MODULE_LICENSE("GPL");