blob: 31b0e17ed090f4316dc39fed9c99e73ba2455893 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090033#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/string.h>
Richard Purdieaac51f02005-09-06 15:19:03 -070035#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/io.h>
37#include <asm/uaccess.h>
38#include <video/w100fb.h>
39#include "w100fb.h"
40
41/*
42 * Prototypes
43 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070044static void w100_suspend(u32 mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070045static void w100_vsync(void);
Richard Purdieaac51f02005-09-06 15:19:03 -070046static void w100_hw_init(struct w100fb_par*);
47static void w100_pwm_setup(struct w100fb_par*);
48static void w100_init_clocks(struct w100fb_par*);
49static void w100_setup_memory(struct w100fb_par*);
50static void w100_init_lcd(struct w100fb_par*);
51static void w100_set_dispregs(struct w100fb_par*);
52static void w100_update_enable(void);
53static void w100_update_disable(void);
54static void calc_hsync(struct w100fb_par *par);
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -080055static void w100_init_graphic_engine(struct w100fb_par *par);
Richard Purdieaac51f02005-09-06 15:19:03 -070056struct w100_pll_info *w100_get_xtal_table(unsigned int freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58/* Pseudo palette size */
59#define MAX_PALETTES 16
60
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define W100_SUSPEND_EXTMEM 0
62#define W100_SUSPEND_ALL 1
63
Richard Purdieaac51f02005-09-06 15:19:03 -070064#define BITS_PER_PIXEL 16
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
67static void *remapped_base;
68static void *remapped_regs;
69static void *remapped_fbuf;
70
Richard Purdieaac51f02005-09-06 15:19:03 -070071#define REMAPPED_FB_LEN 0x15ffff
72
73/* This is the offset in the w100's address space we map the current
74 framebuffer memory to. We use the position of external memory as
75 we can remap internal memory to there if external isn't present. */
76#define W100_FB_BASE MEM_EXT_BASE_VALUE
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79/*
80 * Sysfs functions
81 */
Richard Purdieaac51f02005-09-06 15:19:03 -070082static ssize_t flip_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083{
84 struct fb_info *info = dev_get_drvdata(dev);
85 struct w100fb_par *par=info->par;
86
Richard Purdieaac51f02005-09-06 15:19:03 -070087 return sprintf(buf, "%d\n",par->flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
Richard Purdieaac51f02005-09-06 15:19:03 -070090static ssize_t flip_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070091{
Richard Purdieaac51f02005-09-06 15:19:03 -070092 unsigned int flip;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 struct fb_info *info = dev_get_drvdata(dev);
94 struct w100fb_par *par=info->par;
95
Richard Purdieaac51f02005-09-06 15:19:03 -070096 flip = simple_strtoul(buf, NULL, 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
Richard Purdieaac51f02005-09-06 15:19:03 -070098 if (flip > 0)
99 par->flip = 1;
100 else
101 par->flip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Richard Purdieaac51f02005-09-06 15:19:03 -0700103 w100_update_disable();
104 w100_set_dispregs(par);
105 w100_update_enable();
106
107 calc_hsync(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
109 return count;
110}
111
Richard Purdieaac51f02005-09-06 15:19:03 -0700112static DEVICE_ATTR(flip, 0644, flip_show, flip_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Yani Ioannou060b8842005-05-17 06:44:04 -0400114static 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 -0700115{
Richard Purdieaac51f02005-09-06 15:19:03 -0700116 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 regs = simple_strtoul(buf, NULL, 16);
118 param = readl(remapped_regs + regs);
119 printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
120 return count;
121}
122
123static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
124
Yani Ioannou060b8842005-05-17 06:44:04 -0400125static 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 -0700126{
Richard Purdieaac51f02005-09-06 15:19:03 -0700127 unsigned long regs, param;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 sscanf(buf, "%lx %lx", &regs, &param);
129
130 if (regs <= 0x2000) {
131 printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
132 writel(param, remapped_regs + regs);
133 }
134
135 return count;
136}
137
138static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
139
140
Richard Purdieaac51f02005-09-06 15:19:03 -0700141static ssize_t fastpllclk_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
143 struct fb_info *info = dev_get_drvdata(dev);
144 struct w100fb_par *par=info->par;
145
Richard Purdieaac51f02005-09-06 15:19:03 -0700146 return sprintf(buf, "%d\n",par->fastpll_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
Richard Purdieaac51f02005-09-06 15:19:03 -0700149static ssize_t fastpllclk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 struct fb_info *info = dev_get_drvdata(dev);
152 struct w100fb_par *par=info->par;
153
Richard Purdieaac51f02005-09-06 15:19:03 -0700154 if (simple_strtoul(buf, NULL, 10) > 0) {
155 par->fastpll_mode=1;
156 printk("w100fb: Using fast system clock (if possible)\n");
157 } else {
158 par->fastpll_mode=0;
159 printk("w100fb: Using normal system clock\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700161
162 w100_init_clocks(par);
163 calc_hsync(par);
164
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 return count;
166}
167
Richard Purdieaac51f02005-09-06 15:19:03 -0700168static DEVICE_ATTR(fastpllclk, 0644, fastpllclk_show, fastpllclk_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700171 * Some touchscreens need hsync information from the video driver to
172 * function correctly. We export it here.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700174unsigned long w100fb_get_hsynclen(struct device *dev)
175{
176 struct fb_info *info = dev_get_drvdata(dev);
177 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
Richard Purdieaac51f02005-09-06 15:19:03 -0700179 /* If display is blanked/suspended, hsync isn't active */
180 if (par->blanked)
181 return 0;
182 else
183 return par->hsync_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
Richard Purdieaac51f02005-09-06 15:19:03 -0700185EXPORT_SYMBOL(w100fb_get_hsynclen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Richard Purdieaac51f02005-09-06 15:19:03 -0700187static void w100fb_clear_screen(struct w100fb_par *par)
188{
189 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 -0700190}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
192
193/*
194 * Set a palette value from rgb components
195 */
196static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
197 u_int trans, struct fb_info *info)
198{
199 unsigned int val;
200 int ret = 1;
201
202 /*
203 * If greyscale is true, then we convert the RGB value
204 * to greyscale no matter what visual we are using.
205 */
206 if (info->var.grayscale)
207 red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
208
209 /*
210 * 16-bit True Colour. We encode the RGB value
211 * according to the RGB bitfield information.
212 */
213 if (regno < MAX_PALETTES) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 u32 *pal = info->pseudo_palette;
215
216 val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
217 pal[regno] = val;
218 ret = 0;
219 }
220 return ret;
221}
222
223
224/*
225 * Blank the display based on value in blank_mode
226 */
227static int w100fb_blank(int blank_mode, struct fb_info *info)
228{
Richard Purdieaac51f02005-09-06 15:19:03 -0700229 struct w100fb_par *par = info->par;
230 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
232 switch(blank_mode) {
233
Richard Purdieaac51f02005-09-06 15:19:03 -0700234 case FB_BLANK_NORMAL: /* Normal blanking */
235 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
236 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
237 case FB_BLANK_POWERDOWN: /* Poweroff */
238 if (par->blanked == 0) {
239 if(tg && tg->suspend)
240 tg->suspend(par);
241 par->blanked = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 }
243 break;
244
245 case FB_BLANK_UNBLANK: /* Unblanking */
Richard Purdieaac51f02005-09-06 15:19:03 -0700246 if (par->blanked != 0) {
247 if(tg && tg->resume)
248 tg->resume(par);
249 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251 break;
252 }
253 return 0;
254}
255
Richard Purdieaac51f02005-09-06 15:19:03 -0700256
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800257static void w100_fifo_wait(int entries)
258{
259 union rbbm_status_u status;
260 int i;
261
262 for (i = 0; i < 2000000; i++) {
263 status.val = readl(remapped_regs + mmRBBM_STATUS);
264 if (status.f.cmdfifo_avail >= entries)
265 return;
266 udelay(1);
267 }
268 printk(KERN_ERR "w100fb: FIFO Timeout!\n");
269}
270
271
272static int w100fb_sync(struct fb_info *info)
273{
274 union rbbm_status_u status;
275 int i;
276
277 for (i = 0; i < 2000000; i++) {
278 status.val = readl(remapped_regs + mmRBBM_STATUS);
279 if (!status.f.gui_active)
280 return 0;
281 udelay(1);
282 }
283 printk(KERN_ERR "w100fb: Graphic engine timeout!\n");
284 return -EBUSY;
285}
286
287
288static void w100_init_graphic_engine(struct w100fb_par *par)
289{
290 union dp_gui_master_cntl_u gmc;
291 union dp_mix_u dp_mix;
292 union dp_datatype_u dp_datatype;
293 union dp_cntl_u dp_cntl;
294
295 w100_fifo_wait(4);
296 writel(W100_FB_BASE, remapped_regs + mmDST_OFFSET);
297 writel(par->xres, remapped_regs + mmDST_PITCH);
298 writel(W100_FB_BASE, remapped_regs + mmSRC_OFFSET);
299 writel(par->xres, remapped_regs + mmSRC_PITCH);
300
301 w100_fifo_wait(3);
302 writel(0, remapped_regs + mmSC_TOP_LEFT);
303 writel((par->yres << 16) | par->xres, remapped_regs + mmSC_BOTTOM_RIGHT);
304 writel(0x1fff1fff, remapped_regs + mmSRC_SC_BOTTOM_RIGHT);
305
306 w100_fifo_wait(4);
307 dp_cntl.val = 0;
308 dp_cntl.f.dst_x_dir = 1;
309 dp_cntl.f.dst_y_dir = 1;
310 dp_cntl.f.src_x_dir = 1;
311 dp_cntl.f.src_y_dir = 1;
312 dp_cntl.f.dst_major_x = 1;
313 dp_cntl.f.src_major_x = 1;
314 writel(dp_cntl.val, remapped_regs + mmDP_CNTL);
315
316 gmc.val = 0;
317 gmc.f.gmc_src_pitch_offset_cntl = 1;
318 gmc.f.gmc_dst_pitch_offset_cntl = 1;
319 gmc.f.gmc_src_clipping = 1;
320 gmc.f.gmc_dst_clipping = 1;
321 gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
322 gmc.f.gmc_dst_datatype = 3; /* from DstType_16Bpp_444 */
323 gmc.f.gmc_src_datatype = SRC_DATATYPE_EQU_DST;
324 gmc.f.gmc_byte_pix_order = 1;
325 gmc.f.gmc_default_sel = 0;
326 gmc.f.gmc_rop3 = ROP3_SRCCOPY;
327 gmc.f.gmc_dp_src_source = DP_SRC_MEM_RECTANGULAR;
328 gmc.f.gmc_clr_cmp_fcn_dis = 1;
329 gmc.f.gmc_wr_msk_dis = 1;
330 gmc.f.gmc_dp_op = DP_OP_ROP;
331 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
332
333 dp_datatype.val = dp_mix.val = 0;
334 dp_datatype.f.dp_dst_datatype = gmc.f.gmc_dst_datatype;
335 dp_datatype.f.dp_brush_datatype = gmc.f.gmc_brush_datatype;
336 dp_datatype.f.dp_src2_type = 0;
337 dp_datatype.f.dp_src2_datatype = gmc.f.gmc_src_datatype;
338 dp_datatype.f.dp_src_datatype = gmc.f.gmc_src_datatype;
339 dp_datatype.f.dp_byte_pix_order = gmc.f.gmc_byte_pix_order;
340 writel(dp_datatype.val, remapped_regs + mmDP_DATATYPE);
341
342 dp_mix.f.dp_src_source = gmc.f.gmc_dp_src_source;
343 dp_mix.f.dp_src2_source = 1;
344 dp_mix.f.dp_rop3 = gmc.f.gmc_rop3;
345 dp_mix.f.dp_op = gmc.f.gmc_dp_op;
346 writel(dp_mix.val, remapped_regs + mmDP_MIX);
347}
348
349
350static void w100fb_fillrect(struct fb_info *info,
351 const struct fb_fillrect *rect)
352{
353 union dp_gui_master_cntl_u gmc;
354
355 if (info->state != FBINFO_STATE_RUNNING)
356 return;
357 if (info->flags & FBINFO_HWACCEL_DISABLED) {
358 cfb_fillrect(info, rect);
359 return;
360 }
361
362 gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
363 gmc.f.gmc_rop3 = ROP3_PATCOPY;
364 gmc.f.gmc_brush_datatype = GMC_BRUSH_SOLID_COLOR;
365 w100_fifo_wait(2);
366 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
367 writel(rect->color, remapped_regs + mmDP_BRUSH_FRGD_CLR);
368
369 w100_fifo_wait(2);
370 writel((rect->dy << 16) | (rect->dx & 0xffff), remapped_regs + mmDST_Y_X);
371 writel((rect->width << 16) | (rect->height & 0xffff),
372 remapped_regs + mmDST_WIDTH_HEIGHT);
373}
374
375
376static void w100fb_copyarea(struct fb_info *info,
377 const struct fb_copyarea *area)
378{
379 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
380 u32 h = area->height, w = area->width;
381 union dp_gui_master_cntl_u gmc;
382
383 if (info->state != FBINFO_STATE_RUNNING)
384 return;
385 if (info->flags & FBINFO_HWACCEL_DISABLED) {
386 cfb_copyarea(info, area);
387 return;
388 }
389
390 gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
391 gmc.f.gmc_rop3 = ROP3_SRCCOPY;
392 gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
393 w100_fifo_wait(1);
394 writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
395
396 w100_fifo_wait(3);
397 writel((sy << 16) | (sx & 0xffff), remapped_regs + mmSRC_Y_X);
398 writel((dy << 16) | (dx & 0xffff), remapped_regs + mmDST_Y_X);
399 writel((w << 16) | (h & 0xffff), remapped_regs + mmDST_WIDTH_HEIGHT);
400}
401
402
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403/*
404 * Change the resolution by calling the appropriate hardware functions
405 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700406static void w100fb_activate_var(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407{
Richard Purdieaac51f02005-09-06 15:19:03 -0700408 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
Richard Purdieaac51f02005-09-06 15:19:03 -0700410 w100_pwm_setup(par);
411 w100_setup_memory(par);
412 w100_init_clocks(par);
413 w100fb_clear_screen(par);
414 w100_vsync();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Richard Purdieaac51f02005-09-06 15:19:03 -0700416 w100_update_disable();
417 w100_init_lcd(par);
418 w100_set_dispregs(par);
419 w100_update_enable();
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800420 w100_init_graphic_engine(par);
Richard Purdieaac51f02005-09-06 15:19:03 -0700421
422 calc_hsync(par);
423
424 if (!par->blanked && tg && tg->change)
425 tg->change(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
Richard Purdieaac51f02005-09-06 15:19:03 -0700428
429/* Select the smallest mode that allows the desired resolution to be
430 * displayed. If desired, the x and y parameters can be rounded up to
431 * match the selected mode.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700433static 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 -0700434{
Richard Purdieaac51f02005-09-06 15:19:03 -0700435 struct w100_mode *mode = NULL;
436 struct w100_mode *modelist = par->mach->modelist;
437 unsigned int best_x = 0xffffffff, best_y = 0xffffffff;
438 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
Richard Purdieaac51f02005-09-06 15:19:03 -0700440 for (i = 0 ; i < par->mach->num_modes ; i++) {
441 if (modelist[i].xres >= *x && modelist[i].yres >= *y &&
442 modelist[i].xres < best_x && modelist[i].yres < best_y) {
443 best_x = modelist[i].xres;
444 best_y = modelist[i].yres;
445 mode = &modelist[i];
446 } else if(modelist[i].xres >= *y && modelist[i].yres >= *x &&
447 modelist[i].xres < best_y && modelist[i].yres < best_x) {
448 best_x = modelist[i].yres;
449 best_y = modelist[i].xres;
450 mode = &modelist[i];
451 }
452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Richard Purdieaac51f02005-09-06 15:19:03 -0700454 if (mode && saveval) {
455 *x = best_x;
456 *y = best_y;
457 }
458
459 return mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460}
461
462
463/*
464 * w100fb_check_var():
465 * Get the video params out of 'var'. If a value doesn't fit, round it up,
466 * if it's too big, return -EINVAL.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 */
468static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
469{
Richard Purdieaac51f02005-09-06 15:19:03 -0700470 struct w100fb_par *par=info->par;
471
472 if(!w100fb_get_mode(par, &var->xres, &var->yres, 1))
473 return -EINVAL;
474
475 if (par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (par->mach->mem->size+1)))
476 return -EINVAL;
477
478 if (!par->mach->mem && ((var->xres*var->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)))
479 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481 var->xres_virtual = max(var->xres_virtual, var->xres);
482 var->yres_virtual = max(var->yres_virtual, var->yres);
483
484 if (var->bits_per_pixel > BITS_PER_PIXEL)
485 return -EINVAL;
486 else
487 var->bits_per_pixel = BITS_PER_PIXEL;
488
489 var->red.offset = 11;
490 var->red.length = 5;
491 var->green.offset = 5;
492 var->green.length = 6;
493 var->blue.offset = 0;
494 var->blue.length = 5;
495 var->transp.offset = var->transp.length = 0;
496
497 var->nonstd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 var->height = -1;
499 var->width = -1;
500 var->vmode = FB_VMODE_NONINTERLACED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 var->sync = 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700502 var->pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504 return 0;
505}
506
507
508/*
509 * w100fb_set_par():
510 * Set the user defined part of the display for the specified console
511 * by looking at the values in info.var
512 */
513static int w100fb_set_par(struct fb_info *info)
514{
515 struct w100fb_par *par=info->par;
516
Richard Purdieaac51f02005-09-06 15:19:03 -0700517 if (par->xres != info->var.xres || par->yres != info->var.yres) {
518 par->xres = info->var.xres;
519 par->yres = info->var.yres;
520 par->mode = w100fb_get_mode(par, &par->xres, &par->yres, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Richard Purdieaac51f02005-09-06 15:19:03 -0700522 info->fix.visual = FB_VISUAL_TRUECOLOR;
523 info->fix.ypanstep = 0;
524 info->fix.ywrapstep = 0;
525 info->fix.line_length = par->xres * BITS_PER_PIXEL / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700527 mutex_lock(&info->mm_lock);
Richard Purdieaac51f02005-09-06 15:19:03 -0700528 if ((par->xres*par->yres*BITS_PER_PIXEL/8) > (MEM_INT_SIZE+1)) {
529 par->extmem_active = 1;
530 info->fix.smem_len = par->mach->mem->size+1;
531 } else {
532 par->extmem_active = 0;
533 info->fix.smem_len = MEM_INT_SIZE+1;
534 }
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700535 mutex_unlock(&info->mm_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Richard Purdieaac51f02005-09-06 15:19:03 -0700537 w100fb_activate_var(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 return 0;
540}
541
542
543/*
Richard Purdieaac51f02005-09-06 15:19:03 -0700544 * Frame buffer operations
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 */
546static struct fb_ops w100fb_ops = {
Richard Purdieaac51f02005-09-06 15:19:03 -0700547 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 .fb_check_var = w100fb_check_var,
Richard Purdieaac51f02005-09-06 15:19:03 -0700549 .fb_set_par = w100fb_set_par,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 .fb_setcolreg = w100fb_setcolreg,
Richard Purdieaac51f02005-09-06 15:19:03 -0700551 .fb_blank = w100fb_blank,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800552 .fb_fillrect = w100fb_fillrect,
553 .fb_copyarea = w100fb_copyarea,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 .fb_imageblit = cfb_imageblit,
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800555 .fb_sync = w100fb_sync,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556};
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558#ifdef CONFIG_PM
Richard Purdieaac51f02005-09-06 15:19:03 -0700559static void w100fb_save_vidmem(struct w100fb_par *par)
560{
561 int memsize;
562
563 if (par->extmem_active) {
564 memsize=par->mach->mem->size;
565 par->saved_extmem = vmalloc(memsize);
566 if (par->saved_extmem)
567 memcpy_fromio(par->saved_extmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
568 }
569 memsize=MEM_INT_SIZE;
570 par->saved_intmem = vmalloc(memsize);
571 if (par->saved_intmem && par->extmem_active)
572 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), memsize);
573 else if (par->saved_intmem)
574 memcpy_fromio(par->saved_intmem, remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), memsize);
575}
576
577static void w100fb_restore_vidmem(struct w100fb_par *par)
578{
579 int memsize;
580
581 if (par->extmem_active && par->saved_extmem) {
582 memsize=par->mach->mem->size;
583 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize);
584 vfree(par->saved_extmem);
585 }
586 if (par->saved_intmem) {
587 memsize=MEM_INT_SIZE;
588 if (par->extmem_active)
589 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_INT_BASE_VALUE), par->saved_intmem, memsize);
590 else
591 memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize);
592 vfree(par->saved_intmem);
593 }
594}
595
Russell King3ae5eae2005-11-09 22:32:44 +0000596static int w100fb_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
Russell King3ae5eae2005-11-09 22:32:44 +0000598 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700599 struct w100fb_par *par=info->par;
600 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Russell King9480e302005-10-28 09:52:56 -0700602 w100fb_save_vidmem(par);
603 if(tg && tg->suspend)
604 tg->suspend(par);
605 w100_suspend(W100_SUSPEND_ALL);
606 par->blanked = 1;
607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return 0;
609}
610
Russell King3ae5eae2005-11-09 22:32:44 +0000611static int w100fb_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
Russell King3ae5eae2005-11-09 22:32:44 +0000613 struct fb_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700614 struct w100fb_par *par=info->par;
615 struct w100_tg_info *tg = par->mach->tg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
Russell King9480e302005-10-28 09:52:56 -0700617 w100_hw_init(par);
618 w100fb_activate_var(par);
619 w100fb_restore_vidmem(par);
620 if(tg && tg->resume)
621 tg->resume(par);
622 par->blanked = 0;
623
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 return 0;
625}
626#else
Richard Purdieaac51f02005-09-06 15:19:03 -0700627#define w100fb_suspend NULL
628#define w100fb_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629#endif
630
631
Uwe Kleine-Königc2e13032010-02-04 20:56:51 +0100632int __devinit w100fb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
Richard Purdieaac51f02005-09-06 15:19:03 -0700634 int err = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 struct w100fb_mach_info *inf;
Richard Purdieaac51f02005-09-06 15:19:03 -0700636 struct fb_info *info = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 struct w100fb_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Richard Purdieaac51f02005-09-06 15:19:03 -0700639 unsigned int chip_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 if (!mem)
642 return -EINVAL;
643
Richard Purdieaac51f02005-09-06 15:19:03 -0700644 /* Remap the chip base address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
646 if (remapped_base == NULL)
Richard Purdieaac51f02005-09-06 15:19:03 -0700647 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Richard Purdieaac51f02005-09-06 15:19:03 -0700649 /* Map the register space */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
Richard Purdieaac51f02005-09-06 15:19:03 -0700651 if (remapped_regs == NULL)
652 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Richard Purdieaac51f02005-09-06 15:19:03 -0700654 /* Identify the chip */
655 printk("Found ");
656 chip_id = readl(remapped_regs + mmCHIP_ID);
657 switch(chip_id) {
658 case CHIP_ID_W100: printk("w100"); break;
659 case CHIP_ID_W3200: printk("w3200"); break;
660 case CHIP_ID_W3220: printk("w3220"); break;
661 default:
662 printk("Unknown imageon chip ID\n");
663 err = -ENODEV;
664 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 }
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700666 printk(" at 0x%08lx.\n", (unsigned long) mem->start+W100_CFG_BASE);
Richard Purdieaac51f02005-09-06 15:19:03 -0700667
668 /* Remap the framebuffer */
669 remapped_fbuf = ioremap_nocache(mem->start+MEM_WINDOW_BASE, MEM_WINDOW_SIZE);
670 if (remapped_fbuf == NULL)
671 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Richard Purdieafdd3b32005-11-13 16:06:38 -0800673 info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 if (!info) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700675 err = -ENOMEM;
676 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 }
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 par = info->par;
Russell King3ae5eae2005-11-09 22:32:44 +0000680 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
Russell King3ae5eae2005-11-09 22:32:44 +0000682 inf = pdev->dev.platform_data;
Richard Purdieaac51f02005-09-06 15:19:03 -0700683 par->chip_id = chip_id;
684 par->mach = inf;
685 par->fastpll_mode = 0;
686 par->blanked = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Richard Purdieaac51f02005-09-06 15:19:03 -0700688 par->pll_table=w100_get_xtal_table(inf->xtal_freq);
689 if (!par->pll_table) {
690 printk(KERN_ERR "No matching Xtal definition found\n");
691 err = -EINVAL;
692 goto out;
693 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
695 info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
696 if (!info->pseudo_palette) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700697 err = -ENOMEM;
698 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 }
700
701 info->fbops = &w100fb_ops;
Alberto Mardegan9b0e1c52006-03-31 02:31:46 -0800702 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
703 FBINFO_HWACCEL_FILLRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 info->node = -1;
Richard Purdieaac51f02005-09-06 15:19:03 -0700705 info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 info->screen_size = REMAPPED_FB_LEN;
707
Richard Purdieaac51f02005-09-06 15:19:03 -0700708 strcpy(info->fix.id, "w100fb");
709 info->fix.type = FB_TYPE_PACKED_PIXELS;
710 info->fix.type_aux = 0;
711 info->fix.accel = FB_ACCEL_NONE;
712 info->fix.smem_start = mem->start+W100_FB_BASE;
713 info->fix.mmio_start = mem->start+W100_REG_BASE;
714 info->fix.mmio_len = W100_REG_LEN;
715
716 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
717 err = -ENOMEM;
718 goto out;
719 }
720
721 par->mode = &inf->modelist[0];
722 if(inf->init_mode & INIT_MODE_ROTATED) {
723 info->var.xres = par->mode->yres;
724 info->var.yres = par->mode->xres;
725 }
726 else {
727 info->var.xres = par->mode->xres;
728 info->var.yres = par->mode->yres;
729 }
730
731 if(inf->init_mode &= INIT_MODE_FLIPPED)
732 par->flip = 1;
733 else
734 par->flip = 0;
735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 info->var.xres_virtual = info->var.xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 info->var.yres_virtual = info->var.yres;
Richard Purdieaac51f02005-09-06 15:19:03 -0700738 info->var.pixclock = 0x04; /* 171521; */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 info->var.sync = 0;
740 info->var.grayscale = 0;
741 info->var.xoffset = info->var.yoffset = 0;
742 info->var.accel_flags = 0;
743 info->var.activate = FB_ACTIVATE_NOW;
744
Richard Purdieaac51f02005-09-06 15:19:03 -0700745 w100_hw_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Richard Purdieaac51f02005-09-06 15:19:03 -0700747 if (w100fb_check_var(&info->var, info) < 0) {
748 err = -EINVAL;
749 goto out;
750 }
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 if (register_framebuffer(info) < 0) {
Richard Purdieaac51f02005-09-06 15:19:03 -0700753 err = -EINVAL;
754 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
756
Richard Purdiea01fbbd2007-05-23 13:57:49 -0700757 err = device_create_file(&pdev->dev, &dev_attr_fastpllclk);
758 err |= device_create_file(&pdev->dev, &dev_attr_reg_read);
759 err |= device_create_file(&pdev->dev, &dev_attr_reg_write);
760 err |= device_create_file(&pdev->dev, &dev_attr_flip);
761
762 if (err != 0)
763 printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
764 info->node, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
767 return 0;
Richard Purdieaac51f02005-09-06 15:19:03 -0700768out:
Andrew Morton8224c3b2008-04-28 02:15:45 -0700769 if (info) {
770 fb_dealloc_cmap(&info->cmap);
771 kfree(info->pseudo_palette);
772 }
Richard Purdieaac51f02005-09-06 15:19:03 -0700773 if (remapped_fbuf != NULL)
774 iounmap(remapped_fbuf);
775 if (remapped_regs != NULL)
776 iounmap(remapped_regs);
777 if (remapped_base != NULL)
778 iounmap(remapped_base);
779 if (info)
780 framebuffer_release(info);
781 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782}
783
784
Russell King3ae5eae2005-11-09 22:32:44 +0000785static int w100fb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786{
Russell King3ae5eae2005-11-09 22:32:44 +0000787 struct fb_info *info = platform_get_drvdata(pdev);
Richard Purdieaac51f02005-09-06 15:19:03 -0700788 struct w100fb_par *par=info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Russell King3ae5eae2005-11-09 22:32:44 +0000790 device_remove_file(&pdev->dev, &dev_attr_fastpllclk);
791 device_remove_file(&pdev->dev, &dev_attr_reg_read);
792 device_remove_file(&pdev->dev, &dev_attr_reg_write);
793 device_remove_file(&pdev->dev, &dev_attr_flip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 unregister_framebuffer(info);
796
Richard Purdieaac51f02005-09-06 15:19:03 -0700797 vfree(par->saved_intmem);
798 vfree(par->saved_extmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 kfree(info->pseudo_palette);
Richard Purdieaac51f02005-09-06 15:19:03 -0700800 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 iounmap(remapped_base);
803 iounmap(remapped_regs);
804 iounmap(remapped_fbuf);
805
806 framebuffer_release(info);
807
808 return 0;
809}
810
811
812/* ------------------- chipset specific functions -------------------------- */
813
814
815static void w100_soft_reset(void)
816{
817 u16 val = readw((u16 *) remapped_base + cfgSTATUS);
818 writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
819 udelay(100);
820 writew(0x00, (u16 *) remapped_base + cfgSTATUS);
821 udelay(100);
822}
823
Richard Purdieaac51f02005-09-06 15:19:03 -0700824static void w100_update_disable(void)
825{
826 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
827
828 /* Prevent display updates */
829 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
830 disp_db_buf_wr_cntl.f.update_db_buf = 0;
831 disp_db_buf_wr_cntl.f.en_db_buf = 0;
832 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
833}
834
835static void w100_update_enable(void)
836{
837 union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
838
839 /* Enable display updates */
840 disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
841 disp_db_buf_wr_cntl.f.update_db_buf = 1;
842 disp_db_buf_wr_cntl.f.en_db_buf = 1;
843 writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
844}
845
846unsigned long w100fb_gpio_read(int port)
847{
848 unsigned long value;
849
850 if (port==W100_GPIO_PORT_A)
851 value = readl(remapped_regs + mmGPIO_DATA);
852 else
853 value = readl(remapped_regs + mmGPIO_DATA2);
854
855 return value;
856}
857
858void w100fb_gpio_write(int port, unsigned long value)
859{
860 if (port==W100_GPIO_PORT_A)
861 value = writel(value, remapped_regs + mmGPIO_DATA);
862 else
863 value = writel(value, remapped_regs + mmGPIO_DATA2);
864}
865EXPORT_SYMBOL(w100fb_gpio_read);
866EXPORT_SYMBOL(w100fb_gpio_write);
867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868/*
869 * Initialization of critical w100 hardware
870 */
Richard Purdieaac51f02005-09-06 15:19:03 -0700871static void w100_hw_init(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872{
873 u32 temp32;
874 union cif_cntl_u cif_cntl;
875 union intf_cntl_u intf_cntl;
876 union cfgreg_base_u cfgreg_base;
877 union wrap_top_dir_u wrap_top_dir;
878 union cif_read_dbg_u cif_read_dbg;
879 union cpu_defaults_u cpu_default;
880 union cif_write_dbg_u cif_write_dbg;
881 union wrap_start_dir_u wrap_start_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 union cif_io_u cif_io;
Richard Purdieaac51f02005-09-06 15:19:03 -0700883 struct w100_gpio_regs *gpio = par->mach->gpio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 w100_soft_reset();
886
887 /* This is what the fpga_init code does on reset. May be wrong
888 but there is little info available */
889 writel(0x31, remapped_regs + mmSCRATCH_UMSK);
890 for (temp32 = 0; temp32 < 10000; temp32++)
891 readl(remapped_regs + mmSCRATCH_UMSK);
892 writel(0x30, remapped_regs + mmSCRATCH_UMSK);
893
894 /* Set up CIF */
895 cif_io.val = defCIF_IO;
896 writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
897
898 cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
899 cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
900 cif_write_dbg.f.en_dword_split_to_rbbm = 1;
901 cif_write_dbg.f.dis_timeout_during_rbbm = 1;
902 writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
903
904 cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
905 cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
906 writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
907
908 cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
909 cif_cntl.f.dis_system_bits = 1;
910 cif_cntl.f.dis_mr = 1;
911 cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
912 cif_cntl.f.intb_oe = 1;
913 cif_cntl.f.interrupt_active_high = 1;
914 writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
915
916 /* Setup cfgINTF_CNTL and cfgCPU defaults */
917 intf_cntl.val = defINTF_CNTL;
918 intf_cntl.f.ad_inc_a = 1;
919 intf_cntl.f.ad_inc_b = 1;
920 intf_cntl.f.rd_data_rdy_a = 0;
921 intf_cntl.f.rd_data_rdy_b = 0;
922 writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
923
924 cpu_default.val = defCPU_DEFAULTS;
925 cpu_default.f.access_ind_addr_a = 1;
926 cpu_default.f.access_ind_addr_b = 1;
927 cpu_default.f.access_scratch_reg = 1;
928 cpu_default.f.transition_size = 0;
929 writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
930
931 /* set up the apertures */
932 writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
933
934 cfgreg_base.val = defCFGREG_BASE;
935 cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
936 writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 wrap_start_dir.val = defWRAP_START_DIR;
939 wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
940 writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
941
942 wrap_top_dir.val = defWRAP_TOP_DIR;
943 wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
944 writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
945
946 writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -0700947
948 /* Set the hardware to 565 colour */
949 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
950 temp32 &= 0xff7fffff;
951 temp32 |= 0x00800000;
952 writel(temp32, remapped_regs + mmDISP_DEBUG2);
953
954 /* Initialise the GPIO lines */
955 if (gpio) {
956 writel(gpio->init_data1, remapped_regs + mmGPIO_DATA);
957 writel(gpio->init_data2, remapped_regs + mmGPIO_DATA2);
958 writel(gpio->gpio_dir1, remapped_regs + mmGPIO_CNTL1);
959 writel(gpio->gpio_oe1, remapped_regs + mmGPIO_CNTL2);
960 writel(gpio->gpio_dir2, remapped_regs + mmGPIO_CNTL3);
961 writel(gpio->gpio_oe2, remapped_regs + mmGPIO_CNTL4);
962 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963}
964
965
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966struct power_state {
967 union clk_pin_cntl_u clk_pin_cntl;
968 union pll_ref_fb_div_u pll_ref_fb_div;
969 union pll_cntl_u pll_cntl;
970 union sclk_cntl_u sclk_cntl;
971 union pclk_cntl_u pclk_cntl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 union pwrmgt_cntl_u pwrmgt_cntl;
Richard Purdieaac51f02005-09-06 15:19:03 -0700973 int auto_mode; /* system clock auto changing? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974};
975
976
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977static struct power_state w100_pwr_state;
978
Richard Purdieaac51f02005-09-06 15:19:03 -0700979/* The PLL Fout is determined by (XtalFreq/(M+1)) * ((N_int+1) + (N_fac/8)) */
980
981/* 12.5MHz Crystal PLL Table */
982static struct w100_pll_info xtal_12500000[] = {
983 /*freq M N_int N_fac tfgoal lock_time */
984 { 50, 0, 1, 0, 0xe0, 56}, /* 50.00 MHz */
985 { 75, 0, 5, 0, 0xde, 37}, /* 75.00 MHz */
986 {100, 0, 7, 0, 0xe0, 28}, /* 100.00 MHz */
987 {125, 0, 9, 0, 0xe0, 22}, /* 125.00 MHz */
988 {150, 0, 11, 0, 0xe0, 17}, /* 150.00 MHz */
989 { 0, 0, 0, 0, 0, 0}, /* Terminator */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990};
991
Richard Purdieaac51f02005-09-06 15:19:03 -0700992/* 14.318MHz Crystal PLL Table */
993static struct w100_pll_info xtal_14318000[] = {
994 /*freq M N_int N_fac tfgoal lock_time */
995 { 40, 4, 13, 0, 0xe0, 80}, /* tfgoal guessed */
996 { 50, 1, 6, 0, 0xe0, 64}, /* 50.05 MHz */
997 { 57, 2, 11, 0, 0xe0, 53}, /* tfgoal guessed */
998 { 75, 0, 4, 3, 0xe0, 43}, /* 75.08 MHz */
999 {100, 0, 6, 0, 0xe0, 32}, /* 100.10 MHz */
1000 { 0, 0, 0, 0, 0, 0},
1001};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Richard Purdieaac51f02005-09-06 15:19:03 -07001003/* 16MHz Crystal PLL Table */
1004static struct w100_pll_info xtal_16000000[] = {
1005 /*freq M N_int N_fac tfgoal lock_time */
1006 { 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
Philipp Zabel27c8d952008-07-04 09:59:53 -07001007 { 80, 1, 9, 0, 0xe0, 13}, /* tfgoal guessed */
Richard Purdieaac51f02005-09-06 15:19:03 -07001008 { 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
1009 { 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
1010 { 0, 0, 0, 0, 0, 0},
1011};
1012
1013static struct pll_entries {
1014 int xtal_freq;
1015 struct w100_pll_info *pll_table;
1016} w100_pll_tables[] = {
1017 { 12500000, &xtal_12500000[0] },
1018 { 14318000, &xtal_14318000[0] },
1019 { 16000000, &xtal_16000000[0] },
1020 { 0 },
1021};
1022
1023struct w100_pll_info *w100_get_xtal_table(unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024{
Richard Purdieaac51f02005-09-06 15:19:03 -07001025 struct pll_entries *pll_entry = w100_pll_tables;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Richard Purdieaac51f02005-09-06 15:19:03 -07001027 do {
1028 if (freq == pll_entry->xtal_freq)
1029 return pll_entry->pll_table;
1030 pll_entry++;
1031 } while (pll_entry->xtal_freq);
1032 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033}
1034
1035
Richard Purdieaac51f02005-09-06 15:19:03 -07001036static unsigned int w100_get_testcount(unsigned int testclk_sel)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037{
Richard Purdieaac51f02005-09-06 15:19:03 -07001038 union clk_test_cntl_u clk_test_cntl;
1039
1040 udelay(5);
1041
1042 /* Select the test clock source and reset */
1043 clk_test_cntl.f.start_check_freq = 0x0;
1044 clk_test_cntl.f.testclk_sel = testclk_sel;
1045 clk_test_cntl.f.tstcount_rst = 0x1; /* set reset */
1046 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1047
1048 clk_test_cntl.f.tstcount_rst = 0x0; /* clear reset */
1049 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1050
1051 /* Run clock test */
1052 clk_test_cntl.f.start_check_freq = 0x1;
1053 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1054
1055 /* Give the test time to complete */
1056 udelay(20);
1057
1058 /* Return the result */
1059 clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
1060 clk_test_cntl.f.start_check_freq = 0x0;
1061 writel((u32) (clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
1062
1063 return clk_test_cntl.f.test_count;
1064}
1065
1066
1067static int w100_pll_adjust(struct w100_pll_info *pll)
1068{
1069 unsigned int tf80;
1070 unsigned int tf20;
1071
1072 /* Initial Settings */
1073 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
1074 w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
1075 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
1076 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
1077 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
1078 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
1079 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1080
1081 /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
1082 * therefore, commented out the following lines
1083 * tf80 meant tf100
1084 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 do {
Richard Purdieaac51f02005-09-06 15:19:03 -07001086 /* set VCO input = 0.8 * VDD */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
1088 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1089
Richard Purdieaac51f02005-09-06 15:19:03 -07001090 tf80 = w100_get_testcount(TESTCLK_SRC_PLL);
1091 if (tf80 >= (pll->tfgoal)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 /* set VCO input = 0.2 * VDD */
1093 w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
1094 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1095
Richard Purdieaac51f02005-09-06 15:19:03 -07001096 tf20 = w100_get_testcount(TESTCLK_SRC_PLL);
1097 if (tf20 <= (pll->tfgoal))
1098 return 1; /* Success */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
1100 if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
Richard Purdieaac51f02005-09-06 15:19:03 -07001101 ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
1102 (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 /* slow VCO config */
1104 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
1105 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1106 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 continue;
1108 }
1109 }
1110 if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
1111 w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001112 } else if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1114 w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
Richard Purdieaac51f02005-09-06 15:19:03 -07001115 } else {
1116 return 0; /* Error */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 } while(1);
1119}
1120
1121
1122/*
1123 * w100_pll_calibration
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 */
Richard Purdieaac51f02005-09-06 15:19:03 -07001125static int w100_pll_calibration(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126{
Richard Purdieaac51f02005-09-06 15:19:03 -07001127 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
Richard Purdieaac51f02005-09-06 15:19:03 -07001129 status = w100_pll_adjust(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 /* PLL Reset And Lock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 /* set VCO input = 0.5 * VDD */
1133 w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
1134 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1135
Richard Purdieaac51f02005-09-06 15:19:03 -07001136 udelay(1); /* reset time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
1138 /* enable charge pump */
Richard Purdieaac51f02005-09-06 15:19:03 -07001139 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1141
Richard Purdieaac51f02005-09-06 15:19:03 -07001142 /* set VCO input = Hi-Z, disable DAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
1144 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1145
Richard Purdieaac51f02005-09-06 15:19:03 -07001146 udelay(400); /* lock time */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 /* PLL locked */
1149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return status;
1151}
1152
1153
Richard Purdieaac51f02005-09-06 15:19:03 -07001154static int w100_pll_set_clk(struct w100_pll_info *pll)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155{
Richard Purdieaac51f02005-09-06 15:19:03 -07001156 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Richard Purdieaac51f02005-09-06 15:19:03 -07001158 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001160 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
1161 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1163 }
1164
Richard Purdieaac51f02005-09-06 15:19:03 -07001165 /* Set system clock source to XTAL whilst adjusting the PLL! */
1166 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1168
Richard Purdieaac51f02005-09-06 15:19:03 -07001169 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = pll->M;
1170 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = pll->N_int;
1171 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = pll->N_fac;
1172 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = pll->lock_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1174
1175 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
1176 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1177
Richard Purdieaac51f02005-09-06 15:19:03 -07001178 status = w100_pll_calibration(pll);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
Richard Purdieaac51f02005-09-06 15:19:03 -07001180 if (w100_pwr_state.auto_mode == 1) /* auto mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 {
Richard Purdieaac51f02005-09-06 15:19:03 -07001182 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
1183 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1185 }
1186 return status;
1187}
1188
Richard Purdieaac51f02005-09-06 15:19:03 -07001189/* freq = target frequency of the PLL */
1190static int w100_set_pll_freq(struct w100fb_par *par, unsigned int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191{
Richard Purdieaac51f02005-09-06 15:19:03 -07001192 struct w100_pll_info *pll = par->pll_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
Richard Purdieaac51f02005-09-06 15:19:03 -07001194 do {
1195 if (freq == pll->freq) {
1196 return w100_pll_set_clk(pll);
1197 }
1198 pll++;
1199 } while(pll->freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 return 0;
1201}
1202
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203/* Set up an initial state. Some values/fields set
1204 here will be overwritten. */
Richard Purdieaac51f02005-09-06 15:19:03 -07001205static void w100_pwm_setup(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206{
1207 w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
1208 w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
1209 w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
1210 w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001211 w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = par->mach->xtal_dbl ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
1213 writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
1214
Richard Purdieaac51f02005-09-06 15:19:03 -07001215 w100_pwr_state.sclk_cntl.f.sclk_src_sel = CLK_SRC_XTAL;
1216 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
Richard Purdieaac51f02005-09-06 15:19:03 -07001218 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001220 w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
1221 w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
1222 w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
1223 w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
1224 w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
1225 w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
1226 w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
1227 w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0; /* Dynamic */
1228 w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
1230 w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
1231 w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
1232 w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
1233 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1234
Richard Purdieaac51f02005-09-06 15:19:03 -07001235 w100_pwr_state.pclk_cntl.f.pclk_src_sel = CLK_SRC_XTAL;
1236 w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
1237 w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0; /* Dynamic */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1239
Richard Purdieaac51f02005-09-06 15:19:03 -07001240 w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0; /* M = 1 */
1241 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0; /* N = 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
1243 w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
1244 w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
1245 writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
1246
1247 w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
1248 w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
1249 w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001250 w100_pwr_state.pll_cntl.f.pll_mode = 0x0; /* uses VCO clock */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
1252 w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
1253 w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
1254 w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
1255 w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
1256 w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
1257 w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
1258 w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
1259 w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001260 w100_pwr_state.pll_cntl.f.pll_dactal = 0x0; /* Hi-Z */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
1262 w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
1263 w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
1264 w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
1265 writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
1266
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001268 w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1; /* normal mode (0, 1, 3) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
1270 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
1271 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
Richard Purdieaac51f02005-09-06 15:19:03 -07001272 w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1; /* PM4,ENG */
1273 w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1; /* PM4,ENG */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
1275 w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
1276 writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
1277
Richard Purdieaac51f02005-09-06 15:19:03 -07001278 w100_pwr_state.auto_mode = 0; /* manual mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279}
1280
1281
Richard Purdieaac51f02005-09-06 15:19:03 -07001282/*
1283 * Setup the w100 clocks for the specified mode
1284 */
1285static void w100_init_clocks(struct w100fb_par *par)
1286{
1287 struct w100_mode *mode = par->mode;
1288
1289 if (mode->pixclk_src == CLK_SRC_PLL || mode->sysclk_src == CLK_SRC_PLL)
1290 w100_set_pll_freq(par, (par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq);
1291
1292 w100_pwr_state.sclk_cntl.f.sclk_src_sel = mode->sysclk_src;
1293 w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = mode->sysclk_divider;
1294 w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = mode->sysclk_divider;
1295 writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
1296}
1297
1298static void w100_init_lcd(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
1300 u32 temp32;
Richard Purdieaac51f02005-09-06 15:19:03 -07001301 struct w100_mode *mode = par->mode;
1302 struct w100_gen_regs *regs = par->mach->regs;
1303 union active_h_disp_u active_h_disp;
1304 union active_v_disp_u active_v_disp;
1305 union graphic_h_disp_u graphic_h_disp;
1306 union graphic_v_disp_u graphic_v_disp;
1307 union crtc_total_u crtc_total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Richard Purdieaac51f02005-09-06 15:19:03 -07001309 /* w3200 doesnt like undefined bits being set so zero register values first */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Richard Purdieaac51f02005-09-06 15:19:03 -07001311 active_h_disp.val = 0;
1312 active_h_disp.f.active_h_start=mode->left_margin;
1313 active_h_disp.f.active_h_end=mode->left_margin + mode->xres;
1314 writel(active_h_disp.val, remapped_regs + mmACTIVE_H_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
Richard Purdieaac51f02005-09-06 15:19:03 -07001316 active_v_disp.val = 0;
1317 active_v_disp.f.active_v_start=mode->upper_margin;
1318 active_v_disp.f.active_v_end=mode->upper_margin + mode->yres;
1319 writel(active_v_disp.val, remapped_regs + mmACTIVE_V_DISP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Richard Purdieaac51f02005-09-06 15:19:03 -07001321 graphic_h_disp.val = 0;
1322 graphic_h_disp.f.graphic_h_start=mode->left_margin;
1323 graphic_h_disp.f.graphic_h_end=mode->left_margin + mode->xres;
1324 writel(graphic_h_disp.val, remapped_regs + mmGRAPHIC_H_DISP);
1325
1326 graphic_v_disp.val = 0;
1327 graphic_v_disp.f.graphic_v_start=mode->upper_margin;
1328 graphic_v_disp.f.graphic_v_end=mode->upper_margin + mode->yres;
1329 writel(graphic_v_disp.val, remapped_regs + mmGRAPHIC_V_DISP);
1330
1331 crtc_total.val = 0;
1332 crtc_total.f.crtc_h_total=mode->left_margin + mode->xres + mode->right_margin;
1333 crtc_total.f.crtc_v_total=mode->upper_margin + mode->yres + mode->lower_margin;
1334 writel(crtc_total.val, remapped_regs + mmCRTC_TOTAL);
1335
1336 writel(mode->crtc_ss, remapped_regs + mmCRTC_SS);
1337 writel(mode->crtc_ls, remapped_regs + mmCRTC_LS);
1338 writel(mode->crtc_gs, remapped_regs + mmCRTC_GS);
1339 writel(mode->crtc_vpos_gs, remapped_regs + mmCRTC_VPOS_GS);
1340 writel(mode->crtc_rev, remapped_regs + mmCRTC_REV);
1341 writel(mode->crtc_dclk, remapped_regs + mmCRTC_DCLK);
1342 writel(mode->crtc_gclk, remapped_regs + mmCRTC_GCLK);
1343 writel(mode->crtc_goe, remapped_regs + mmCRTC_GOE);
1344 writel(mode->crtc_ps1_active, remapped_regs + mmCRTC_PS1_ACTIVE);
1345
1346 writel(regs->lcd_format, remapped_regs + mmLCD_FORMAT);
1347 writel(regs->lcdd_cntl1, remapped_regs + mmLCDD_CNTL1);
1348 writel(regs->lcdd_cntl2, remapped_regs + mmLCDD_CNTL2);
1349 writel(regs->genlcd_cntl1, remapped_regs + mmGENLCD_CNTL1);
1350 writel(regs->genlcd_cntl2, remapped_regs + mmGENLCD_CNTL2);
1351 writel(regs->genlcd_cntl3, remapped_regs + mmGENLCD_CNTL3);
1352
1353 writel(0x00000000, remapped_regs + mmCRTC_FRAME);
1354 writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
1355 writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
1356 writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358 /* Hack for overlay in ext memory */
1359 temp32 = readl(remapped_regs + mmDISP_DEBUG2);
1360 temp32 |= 0xc0000000;
1361 writel(temp32, remapped_regs + mmDISP_DEBUG2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362}
1363
1364
Richard Purdieaac51f02005-09-06 15:19:03 -07001365static void w100_setup_memory(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366{
Richard Purdieaac51f02005-09-06 15:19:03 -07001367 union mc_ext_mem_location_u extmem_location;
1368 union mc_fb_location_u intmem_location;
1369 struct w100_mem_info *mem = par->mach->mem;
1370 struct w100_bm_mem_info *bm_mem = par->mach->bm_mem;
1371
1372 if (!par->extmem_active) {
1373 w100_suspend(W100_SUSPEND_EXTMEM);
1374
1375 /* Map Internal Memory at FB Base */
1376 intmem_location.f.mc_fb_start = W100_FB_BASE >> 8;
1377 intmem_location.f.mc_fb_top = (W100_FB_BASE+MEM_INT_SIZE) >> 8;
1378 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1379
1380 /* Unmap External Memory - value is *probably* irrelevant but may have meaning
1381 to acceleration libraries */
1382 extmem_location.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
1383 extmem_location.f.mc_ext_mem_top = (MEM_EXT_BASE_VALUE-1) >> 8;
1384 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1385 } else {
1386 /* Map Internal Memory to its default location */
1387 intmem_location.f.mc_fb_start = MEM_INT_BASE_VALUE >> 8;
1388 intmem_location.f.mc_fb_top = (MEM_INT_BASE_VALUE+MEM_INT_SIZE) >> 8;
1389 writel((u32) (intmem_location.val), remapped_regs + mmMC_FB_LOCATION);
1390
1391 /* Map External Memory at FB Base */
1392 extmem_location.f.mc_ext_mem_start = W100_FB_BASE >> 8;
1393 extmem_location.f.mc_ext_mem_top = (W100_FB_BASE+par->mach->mem->size) >> 8;
1394 writel((u32) (extmem_location.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
1395
1396 writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
1397 writel(mem->ext_cntl, remapped_regs + mmMEM_EXT_CNTL);
1398 writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1399 udelay(100);
1400 writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
1401 udelay(100);
1402 writel(mem->sdram_mode_reg, remapped_regs + mmMEM_SDRAM_MODE_REG);
1403 udelay(100);
1404 writel(mem->ext_timing_cntl, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1405 writel(mem->io_cntl, remapped_regs + mmMEM_IO_CNTL);
1406 if (bm_mem) {
1407 writel(bm_mem->ext_mem_bw, remapped_regs + mmBM_EXT_MEM_BANDWIDTH);
1408 writel(bm_mem->offset, remapped_regs + mmBM_OFFSET);
1409 writel(bm_mem->ext_timing_ctl, remapped_regs + mmBM_MEM_EXT_TIMING_CNTL);
1410 writel(bm_mem->ext_cntl, remapped_regs + mmBM_MEM_EXT_CNTL);
1411 writel(bm_mem->mode_reg, remapped_regs + mmBM_MEM_MODE_REG);
1412 writel(bm_mem->io_cntl, remapped_regs + mmBM_MEM_IO_CNTL);
1413 writel(bm_mem->config, remapped_regs + mmBM_CONFIG);
1414 }
1415 }
1416}
1417
1418static void w100_set_dispregs(struct w100fb_par *par)
1419{
1420 unsigned long rot=0, divider, offset=0;
1421 union graphic_ctrl_u graphic_ctrl;
1422
1423 /* See if the mode has been rotated */
1424 if (par->xres == par->mode->xres) {
1425 if (par->flip) {
1426 rot=3; /* 180 degree */
1427 offset=(par->xres * par->yres) - 1;
1428 } /* else 0 degree */
1429 divider = par->mode->pixclk_divider;
1430 } else {
1431 if (par->flip) {
1432 rot=2; /* 270 degree */
1433 offset=par->xres - 1;
1434 } else {
1435 rot=1; /* 90 degree */
1436 offset=par->xres * (par->yres - 1);
1437 }
1438 divider = par->mode->pixclk_divider_rotated;
1439 }
1440
1441 graphic_ctrl.val = 0; /* w32xx doesn't like undefined bits */
1442 switch (par->chip_id) {
1443 case CHIP_ID_W100:
1444 graphic_ctrl.f_w100.color_depth=6;
1445 graphic_ctrl.f_w100.en_crtc=1;
1446 graphic_ctrl.f_w100.en_graphic_req=1;
1447 graphic_ctrl.f_w100.en_graphic_crtc=1;
1448 graphic_ctrl.f_w100.lcd_pclk_on=1;
1449 graphic_ctrl.f_w100.lcd_sclk_on=1;
1450 graphic_ctrl.f_w100.low_power_on=0;
1451 graphic_ctrl.f_w100.req_freq=0;
1452 graphic_ctrl.f_w100.portrait_mode=rot;
1453
1454 /* Zaurus needs this */
1455 switch(par->xres) {
1456 case 240:
1457 case 320:
1458 default:
1459 graphic_ctrl.f_w100.total_req_graphic=0xa0;
1460 break;
1461 case 480:
1462 case 640:
1463 switch(rot) {
1464 case 0: /* 0 */
1465 case 3: /* 180 */
1466 graphic_ctrl.f_w100.low_power_on=1;
1467 graphic_ctrl.f_w100.req_freq=5;
1468 break;
1469 case 1: /* 90 */
1470 case 2: /* 270 */
1471 graphic_ctrl.f_w100.req_freq=4;
1472 break;
1473 default:
1474 break;
1475 }
1476 graphic_ctrl.f_w100.total_req_graphic=0xf0;
1477 break;
1478 }
1479 break;
1480 case CHIP_ID_W3200:
1481 case CHIP_ID_W3220:
1482 graphic_ctrl.f_w32xx.color_depth=6;
1483 graphic_ctrl.f_w32xx.en_crtc=1;
1484 graphic_ctrl.f_w32xx.en_graphic_req=1;
1485 graphic_ctrl.f_w32xx.en_graphic_crtc=1;
1486 graphic_ctrl.f_w32xx.lcd_pclk_on=1;
1487 graphic_ctrl.f_w32xx.lcd_sclk_on=1;
1488 graphic_ctrl.f_w32xx.low_power_on=0;
1489 graphic_ctrl.f_w32xx.req_freq=0;
1490 graphic_ctrl.f_w32xx.total_req_graphic=par->mode->xres >> 1; /* panel xres, not mode */
1491 graphic_ctrl.f_w32xx.portrait_mode=rot;
1492 break;
1493 }
1494
1495 /* Set the pixel clock source and divider */
1496 w100_pwr_state.pclk_cntl.f.pclk_src_sel = par->mode->pixclk_src;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
1498 writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
1499
Richard Purdieaac51f02005-09-06 15:19:03 -07001500 writel(graphic_ctrl.val, remapped_regs + mmGRAPHIC_CTRL);
1501 writel(W100_FB_BASE + ((offset * BITS_PER_PIXEL/8)&~0x03UL), remapped_regs + mmGRAPHIC_OFFSET);
1502 writel((par->xres*BITS_PER_PIXEL/8), remapped_regs + mmGRAPHIC_PITCH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503}
1504
1505
Richard Purdieaac51f02005-09-06 15:19:03 -07001506/*
1507 * Work out how long the sync pulse lasts
1508 * Value is 1/(time in seconds)
1509 */
1510static void calc_hsync(struct w100fb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
Richard Purdieaac51f02005-09-06 15:19:03 -07001512 unsigned long hsync;
1513 struct w100_mode *mode = par->mode;
1514 union crtc_ss_u crtc_ss;
1515
1516 if (mode->pixclk_src == CLK_SRC_XTAL)
1517 hsync=par->mach->xtal_freq;
1518 else
1519 hsync=((par->fastpll_mode && mode->fast_pll_freq) ? mode->fast_pll_freq : mode->pll_freq)*100000;
1520
1521 hsync /= (w100_pwr_state.pclk_cntl.f.pclk_post_div + 1);
1522
1523 crtc_ss.val = readl(remapped_regs + mmCRTC_SS);
1524 if (crtc_ss.val)
1525 par->hsync_len = hsync / (crtc_ss.f.ss_end-crtc_ss.f.ss_start);
1526 else
1527 par->hsync_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528}
1529
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530static void w100_suspend(u32 mode)
1531{
1532 u32 val;
1533
1534 writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
1535 writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
1536
1537 val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001538 val &= ~(0x00100000); /* bit20=0 */
1539 val |= 0xFF000000; /* bit31:24=0xff */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
1541
1542 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001543 val &= ~(0x00040000); /* bit18=0 */
1544 val |= 0x00080000; /* bit19=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1546
Richard Purdieaac51f02005-09-06 15:19:03 -07001547 udelay(1); /* wait 1us */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
1549 if (mode == W100_SUSPEND_EXTMEM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 /* CKE: Tri-State */
1551 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001552 val |= 0x40000000; /* bit30=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1554
1555 /* CLK: Stop */
1556 val = readl(remapped_regs + mmMEM_EXT_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001557 val &= ~(0x00000001); /* bit0=0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 writel(val, remapped_regs + mmMEM_EXT_CNTL);
1559 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 writel(0x00000000, remapped_regs + mmSCLK_CNTL);
1561 writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
1562 writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
1563
1564 udelay(5);
1565
1566 val = readl(remapped_regs + mmPLL_CNTL);
Richard Purdieaac51f02005-09-06 15:19:03 -07001567 val |= 0x00000004; /* bit2=1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 writel(val, remapped_regs + mmPLL_CNTL);
1569 writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
1570 }
1571}
1572
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573static void w100_vsync(void)
1574{
1575 u32 tmp;
Richard Purdieaac51f02005-09-06 15:19:03 -07001576 int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578 tmp = readl(remapped_regs + mmACTIVE_V_DISP);
1579
1580 /* set vline pos */
1581 writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
1582
1583 /* disable vline irq */
1584 tmp = readl(remapped_regs + mmGEN_INT_CNTL);
1585
1586 tmp &= ~0x00000002;
1587 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1588
1589 /* clear vline irq status */
1590 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1591
1592 /* enable vline irq */
1593 writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
1594
1595 /* clear vline irq status */
1596 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1597
1598 while(timeout > 0) {
1599 if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
1600 break;
1601 udelay(1);
1602 timeout--;
1603 }
1604
1605 /* disable vline irq */
1606 writel(tmp, remapped_regs + mmGEN_INT_CNTL);
1607
1608 /* clear vline irq status */
1609 writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
1610}
1611
Russell King3ae5eae2005-11-09 22:32:44 +00001612static struct platform_driver w100fb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 .probe = w100fb_probe,
1614 .remove = w100fb_remove,
1615 .suspend = w100fb_suspend,
1616 .resume = w100fb_resume,
Russell King3ae5eae2005-11-09 22:32:44 +00001617 .driver = {
1618 .name = "w100fb",
1619 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620};
1621
1622int __devinit w100fb_init(void)
1623{
Russell King3ae5eae2005-11-09 22:32:44 +00001624 return platform_driver_register(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625}
1626
1627void __exit w100fb_cleanup(void)
1628{
Russell King3ae5eae2005-11-09 22:32:44 +00001629 platform_driver_unregister(&w100fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630}
1631
1632module_init(w100fb_init);
1633module_exit(w100fb_cleanup);
1634
1635MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
Richard Purdieaac51f02005-09-06 15:19:03 -07001636MODULE_LICENSE("GPL");