~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/video/fb-puv3.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Frame Buffer Driver for PKUnity-v3 Unigfx
 
3
 * Code specific to PKUnity SoC and UniCore ISA
 
4
 *
 
5
 *      Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
 
6
 *      Copyright (C) 2001-2010 Guan Xuetao
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License version 2 as
 
10
 * published by the Free Software Foundation.
 
11
 */
 
12
 
 
13
#include <linux/module.h>
 
14
#include <linux/kernel.h>
 
15
#include <linux/errno.h>
 
16
#include <linux/platform_device.h>
 
17
#include <linux/clk.h>
 
18
#include <linux/fb.h>
 
19
#include <linux/init.h>
 
20
#include <linux/console.h>
 
21
 
 
22
#include <asm/sizes.h>
 
23
#include <mach/hardware.h>
 
24
 
 
25
/* Platform_data reserved for unifb registers. */
 
26
#define UNIFB_REGS_NUM          10
 
27
/* RAM reserved for the frame buffer. */
 
28
#define UNIFB_MEMSIZE           (SZ_4M)         /* 4 MB for 1024*768*32b */
 
29
 
 
30
/*
 
31
 * cause UNIGFX don not have EDID
 
32
 * all the modes are organized as follow
 
33
 */
 
34
static const struct fb_videomode unifb_modes[] = {
 
35
        /* 0 640x480-60 VESA */
 
36
        { "640x480@60",  60,  640, 480,  25175000,  48, 16, 34, 10,  96, 1,
 
37
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
38
        /* 1 640x480-75 VESA */
 
39
        { "640x480@75",  75,  640, 480,  31500000, 120, 16, 18,  1,  64, 1,
 
40
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
41
        /* 2 800x600-60 VESA */
 
42
        { "800x600@60",  60,  800, 600,  40000000,  88, 40, 26,  1, 128, 1,
 
43
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
44
        /* 3 800x600-75 VESA */
 
45
        { "800x600@75",  75,  800, 600,  49500000, 160, 16, 23,  1,  80, 1,
 
46
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
47
        /* 4 1024x768-60 VESA */
 
48
        { "1024x768@60", 60, 1024, 768,  65000000, 160, 24, 34,  3, 136, 1,
 
49
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
50
        /* 5 1024x768-75 VESA */
 
51
        { "1024x768@75", 75, 1024, 768,  78750000, 176, 16, 30,  1,  96, 1,
 
52
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
53
        /* 6 1280x960-60 VESA */
 
54
        { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38,  1, 112, 1,
 
55
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
56
        /* 7 1440x900-60 VESA */
 
57
        { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30,  3, 152, 1,
 
58
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
59
        /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
 
60
        { "1024x600@60", 60, 1024, 600,  50650000, 160, 24, 26,  1, 136, 1,
 
61
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
62
        /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
 
63
        { "1024x600@75", 75, 1024, 600,  61500000, 176, 16, 23,  1,  96, 1,
 
64
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
65
        /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
 
66
        { "1366x768@60", 60, 1366, 768,  85500000, 256, 58, 18,  1,  112, 3,
 
67
          0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
 
68
};
 
69
 
 
70
static struct fb_var_screeninfo unifb_default = {
 
71
        .xres =         640,
 
72
        .yres =         480,
 
73
        .xres_virtual = 640,
 
74
        .yres_virtual = 480,
 
75
        .bits_per_pixel = 16,
 
76
        .red =          { 11, 5, 0 },
 
77
        .green =        { 5,  6, 0 },
 
78
        .blue =         { 0,  5, 0 },
 
79
        .activate =     FB_ACTIVATE_NOW,
 
80
        .height =       -1,
 
81
        .width =        -1,
 
82
        .pixclock =     25175000,
 
83
        .left_margin =  48,
 
84
        .right_margin = 16,
 
85
        .upper_margin = 33,
 
86
        .lower_margin = 10,
 
87
        .hsync_len =    96,
 
88
        .vsync_len =    2,
 
89
        .vmode =        FB_VMODE_NONINTERLACED,
 
90
};
 
91
 
 
92
static struct fb_fix_screeninfo unifb_fix = {
 
93
        .id =           "UNIGFX FB",
 
94
        .type =         FB_TYPE_PACKED_PIXELS,
 
95
        .visual =       FB_VISUAL_TRUECOLOR,
 
96
        .xpanstep =     1,
 
97
        .ypanstep =     1,
 
98
        .ywrapstep =    1,
 
99
        .accel =        FB_ACCEL_NONE,
 
100
};
 
101
 
 
102
static void unifb_sync(struct fb_info *info)
 
103
{
 
104
        /* TODO: may, this can be replaced by interrupt */
 
105
        int cnt;
 
106
 
 
107
        for (cnt = 0; cnt < 0x10000000; cnt++) {
 
108
                if (readl(UGE_COMMAND) & 0x1000000)
 
109
                        return;
 
110
        }
 
111
 
 
112
        if (cnt > 0x8000000)
 
113
                dev_warn(info->device, "Warning: UniGFX GE time out ...\n");
 
114
}
 
115
 
 
116
static void unifb_prim_fillrect(struct fb_info *info,
 
117
                                const struct fb_fillrect *region)
 
118
{
 
119
        int awidth = region->width;
 
120
        int aheight = region->height;
 
121
        int m_iBpp = info->var.bits_per_pixel;
 
122
        int screen_width = info->var.xres;
 
123
        int src_sel = 1;        /* from fg_color */
 
124
        int pat_sel = 1;
 
125
        int src_x0 = 0;
 
126
        int dst_x0 = region->dx;
 
127
        int src_y0 = 0;
 
128
        int dst_y0 = region->dy;
 
129
        int rop_alpha_sel = 0;
 
130
        int rop_alpha_code = 0xCC;
 
131
        int x_dir = 1;
 
132
        int y_dir = 1;
 
133
        int alpha_r = 0;
 
134
        int alpha_sel = 0;
 
135
        int dst_pitch = screen_width * (m_iBpp / 8);
 
136
        int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
 
137
        int src_pitch = screen_width * (m_iBpp / 8);
 
138
        int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
 
139
        unsigned int command = 0;
 
140
        int clip_region = 0;
 
141
        int clip_en = 0;
 
142
        int tp_en = 0;
 
143
        int fg_color = 0;
 
144
        int bottom = info->var.yres - 1;
 
145
        int right = info->var.xres - 1;
 
146
        int top = 0;
 
147
 
 
148
        bottom = (bottom << 16) | right;
 
149
        command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16)
 
150
                | (x_dir << 20) | (y_dir << 21) | (command << 24)
 
151
                | (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
 
152
        src_pitch = (dst_pitch << 16) | src_pitch;
 
153
        awidth = awidth | (aheight << 16);
 
154
        alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff)
 
155
                | (alpha_sel << 16);
 
156
        src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
 
157
        dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
 
158
        fg_color = region->color;
 
159
 
 
160
        unifb_sync(info);
 
161
 
 
162
        writel(((u32 *)(info->pseudo_palette))[fg_color], UGE_FCOLOR);
 
163
        writel(0, UGE_BCOLOR);
 
164
        writel(src_pitch, UGE_PITCH);
 
165
        writel(src_offset, UGE_SRCSTART);
 
166
        writel(dst_offset, UGE_DSTSTART);
 
167
        writel(awidth, UGE_WIDHEIGHT);
 
168
        writel(top, UGE_CLIP0);
 
169
        writel(bottom, UGE_CLIP1);
 
170
        writel(alpha_r, UGE_ROPALPHA);
 
171
        writel(src_x0, UGE_SRCXY);
 
172
        writel(dst_x0, UGE_DSTXY);
 
173
        writel(command, UGE_COMMAND);
 
174
}
 
175
 
 
176
static void unifb_fillrect(struct fb_info *info,
 
177
                const struct fb_fillrect *region)
 
178
{
 
179
        struct fb_fillrect modded;
 
180
        int vxres, vyres;
 
181
 
 
182
        if (info->flags & FBINFO_HWACCEL_DISABLED) {
 
183
                sys_fillrect(info, region);
 
184
                return;
 
185
        }
 
186
 
 
187
        vxres = info->var.xres_virtual;
 
188
        vyres = info->var.yres_virtual;
 
189
 
 
190
        memcpy(&modded, region, sizeof(struct fb_fillrect));
 
191
 
 
192
        if (!modded.width || !modded.height ||
 
193
            modded.dx >= vxres || modded.dy >= vyres)
 
194
                return;
 
195
 
 
196
        if (modded.dx + modded.width > vxres)
 
197
                modded.width = vxres - modded.dx;
 
198
        if (modded.dy + modded.height > vyres)
 
199
                modded.height = vyres - modded.dy;
 
200
 
 
201
        unifb_prim_fillrect(info, &modded);
 
202
}
 
203
 
 
204
static void unifb_prim_copyarea(struct fb_info *info,
 
205
                                const struct fb_copyarea *area)
 
206
{
 
207
        int awidth = area->width;
 
208
        int aheight = area->height;
 
209
        int m_iBpp = info->var.bits_per_pixel;
 
210
        int screen_width = info->var.xres;
 
211
        int src_sel = 2;        /* from mem */
 
212
        int pat_sel = 0;
 
213
        int src_x0 = area->sx;
 
214
        int dst_x0 = area->dx;
 
215
        int src_y0 = area->sy;
 
216
        int dst_y0 = area->dy;
 
217
 
 
218
        int rop_alpha_sel = 0;
 
219
        int rop_alpha_code = 0xCC;
 
220
        int x_dir = 1;
 
221
        int y_dir = 1;
 
222
 
 
223
        int alpha_r = 0;
 
224
        int alpha_sel = 0;
 
225
        int dst_pitch = screen_width * (m_iBpp / 8);
 
226
        int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
 
227
        int src_pitch = screen_width * (m_iBpp / 8);
 
228
        int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
 
229
        unsigned int command = 0;
 
230
        int clip_region = 0;
 
231
        int clip_en = 1;
 
232
        int tp_en = 0;
 
233
        int top = 0;
 
234
        int bottom = info->var.yres;
 
235
        int right = info->var.xres;
 
236
        int fg_color = 0;
 
237
        int bg_color = 0;
 
238
 
 
239
        if (src_x0 < 0)
 
240
                src_x0 = 0;
 
241
        if (src_y0 < 0)
 
242
                src_y0 = 0;
 
243
 
 
244
        if (src_y0 - dst_y0 > 0) {
 
245
                y_dir = 1;
 
246
        } else {
 
247
                y_dir = 0;
 
248
                src_offset = (src_y0 + aheight) * src_pitch +
 
249
                                src_x0 * (m_iBpp / 8);
 
250
                dst_offset = (dst_y0 + aheight) * dst_pitch +
 
251
                                dst_x0 * (m_iBpp / 8);
 
252
                src_y0 += aheight;
 
253
                dst_y0 += aheight;
 
254
        }
 
255
 
 
256
        command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16) |
 
257
                (x_dir << 20) | (y_dir << 21) | (command << 24) |
 
258
                (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
 
259
        src_pitch = (dst_pitch << 16) | src_pitch;
 
260
        awidth = awidth | (aheight << 16);
 
261
        alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff) |
 
262
                (alpha_sel << 16);
 
263
        src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
 
264
        dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
 
265
        bottom = (bottom << 16) | right;
 
266
 
 
267
        unifb_sync(info);
 
268
 
 
269
        writel(src_pitch, UGE_PITCH);
 
270
        writel(src_offset, UGE_SRCSTART);
 
271
        writel(dst_offset, UGE_DSTSTART);
 
272
        writel(awidth, UGE_WIDHEIGHT);
 
273
        writel(top, UGE_CLIP0);
 
274
        writel(bottom, UGE_CLIP1);
 
275
        writel(bg_color, UGE_BCOLOR);
 
276
        writel(fg_color, UGE_FCOLOR);
 
277
        writel(alpha_r, UGE_ROPALPHA);
 
278
        writel(src_x0, UGE_SRCXY);
 
279
        writel(dst_x0, UGE_DSTXY);
 
280
        writel(command, UGE_COMMAND);
 
281
}
 
282
 
 
283
static void unifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
 
284
{
 
285
        struct fb_copyarea modded;
 
286
        u32 vxres, vyres;
 
287
        modded.sx = area->sx;
 
288
        modded.sy = area->sy;
 
289
        modded.dx = area->dx;
 
290
        modded.dy = area->dy;
 
291
        modded.width = area->width;
 
292
        modded.height = area->height;
 
293
 
 
294
        if (info->flags & FBINFO_HWACCEL_DISABLED) {
 
295
                sys_copyarea(info, area);
 
296
                return;
 
297
        }
 
298
 
 
299
        vxres = info->var.xres_virtual;
 
300
        vyres = info->var.yres_virtual;
 
301
 
 
302
        if (!modded.width || !modded.height ||
 
303
            modded.sx >= vxres || modded.sy >= vyres ||
 
304
            modded.dx >= vxres || modded.dy >= vyres)
 
305
                return;
 
306
 
 
307
        if (modded.sx + modded.width > vxres)
 
308
                modded.width = vxres - modded.sx;
 
309
        if (modded.dx + modded.width > vxres)
 
310
                modded.width = vxres - modded.dx;
 
311
        if (modded.sy + modded.height > vyres)
 
312
                modded.height = vyres - modded.sy;
 
313
        if (modded.dy + modded.height > vyres)
 
314
                modded.height = vyres - modded.dy;
 
315
 
 
316
        unifb_prim_copyarea(info, &modded);
 
317
}
 
318
 
 
319
static void unifb_imageblit(struct fb_info *info, const struct fb_image *image)
 
320
{
 
321
        sys_imageblit(info, image);
 
322
}
 
323
 
 
324
static u_long get_line_length(int xres_virtual, int bpp)
 
325
{
 
326
        u_long length;
 
327
 
 
328
        length = xres_virtual * bpp;
 
329
        length = (length + 31) & ~31;
 
330
        length >>= 3;
 
331
        return length;
 
332
}
 
333
 
 
334
/*
 
335
 *  Setting the video mode has been split into two parts.
 
336
 *  First part, xxxfb_check_var, must not write anything
 
337
 *  to hardware, it should only verify and adjust var.
 
338
 *  This means it doesn't alter par but it does use hardware
 
339
 *  data from it to check this var.
 
340
 */
 
341
static int unifb_check_var(struct fb_var_screeninfo *var,
 
342
                         struct fb_info *info)
 
343
{
 
344
        u_long line_length;
 
345
 
 
346
        /*
 
347
         *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
 
348
         *  as FB_VMODE_SMOOTH_XPAN is only used internally
 
349
         */
 
350
 
 
351
        if (var->vmode & FB_VMODE_CONUPDATE) {
 
352
                var->vmode |= FB_VMODE_YWRAP;
 
353
                var->xoffset = info->var.xoffset;
 
354
                var->yoffset = info->var.yoffset;
 
355
        }
 
356
 
 
357
        /*
 
358
         *  Some very basic checks
 
359
         */
 
360
        if (!var->xres)
 
361
                var->xres = 1;
 
362
        if (!var->yres)
 
363
                var->yres = 1;
 
364
        if (var->xres > var->xres_virtual)
 
365
                var->xres_virtual = var->xres;
 
366
        if (var->yres > var->yres_virtual)
 
367
                var->yres_virtual = var->yres;
 
368
        if (var->bits_per_pixel <= 1)
 
369
                var->bits_per_pixel = 1;
 
370
        else if (var->bits_per_pixel <= 8)
 
371
                var->bits_per_pixel = 8;
 
372
        else if (var->bits_per_pixel <= 16)
 
373
                var->bits_per_pixel = 16;
 
374
        else if (var->bits_per_pixel <= 24)
 
375
                var->bits_per_pixel = 24;
 
376
        else if (var->bits_per_pixel <= 32)
 
377
                var->bits_per_pixel = 32;
 
378
        else
 
379
                return -EINVAL;
 
380
 
 
381
        if (var->xres_virtual < var->xoffset + var->xres)
 
382
                var->xres_virtual = var->xoffset + var->xres;
 
383
        if (var->yres_virtual < var->yoffset + var->yres)
 
384
                var->yres_virtual = var->yoffset + var->yres;
 
385
 
 
386
        /*
 
387
         *  Memory limit
 
388
         */
 
389
        line_length =
 
390
            get_line_length(var->xres_virtual, var->bits_per_pixel);
 
391
        if (line_length * var->yres_virtual > UNIFB_MEMSIZE)
 
392
                return -ENOMEM;
 
393
 
 
394
        /*
 
395
         * Now that we checked it we alter var. The reason being is that the
 
396
         * video mode passed in might not work but slight changes to it might
 
397
         * make it work. This way we let the user know what is acceptable.
 
398
         */
 
399
        switch (var->bits_per_pixel) {
 
400
        case 1:
 
401
        case 8:
 
402
                var->red.offset = 0;
 
403
                var->red.length = 8;
 
404
                var->green.offset = 0;
 
405
                var->green.length = 8;
 
406
                var->blue.offset = 0;
 
407
                var->blue.length = 8;
 
408
                var->transp.offset = 0;
 
409
                var->transp.length = 0;
 
410
                break;
 
411
        case 16:                /* RGBA 5551 */
 
412
                if (var->transp.length) {
 
413
                        var->red.offset = 0;
 
414
                        var->red.length = 5;
 
415
                        var->green.offset = 5;
 
416
                        var->green.length = 5;
 
417
                        var->blue.offset = 10;
 
418
                        var->blue.length = 5;
 
419
                        var->transp.offset = 15;
 
420
                        var->transp.length = 1;
 
421
                } else {        /* RGB 565 */
 
422
                        var->red.offset = 11;
 
423
                        var->red.length = 5;
 
424
                        var->green.offset = 5;
 
425
                        var->green.length = 6;
 
426
                        var->blue.offset = 0;
 
427
                        var->blue.length = 5;
 
428
                        var->transp.offset = 0;
 
429
                        var->transp.length = 0;
 
430
                }
 
431
                break;
 
432
        case 24:                /* RGB 888 */
 
433
                var->red.offset = 0;
 
434
                var->red.length = 8;
 
435
                var->green.offset = 8;
 
436
                var->green.length = 8;
 
437
                var->blue.offset = 16;
 
438
                var->blue.length = 8;
 
439
                var->transp.offset = 0;
 
440
                var->transp.length = 0;
 
441
                break;
 
442
        case 32:                /* RGBA 8888 */
 
443
                var->red.offset = 16;
 
444
                var->red.length = 8;
 
445
                var->green.offset = 8;
 
446
                var->green.length = 8;
 
447
                var->blue.offset = 0;
 
448
                var->blue.length = 8;
 
449
                var->transp.offset = 24;
 
450
                var->transp.length = 8;
 
451
                break;
 
452
        }
 
453
        var->red.msb_right = 0;
 
454
        var->green.msb_right = 0;
 
455
        var->blue.msb_right = 0;
 
456
        var->transp.msb_right = 0;
 
457
 
 
458
        return 0;
 
459
}
 
460
 
 
461
/*
 
462
 * This routine actually sets the video mode. It's in here where we
 
463
 * the hardware state info->par and fix which can be affected by the
 
464
 * change in par. For this driver it doesn't do much.
 
465
 */
 
466
static int unifb_set_par(struct fb_info *info)
 
467
{
 
468
        int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
 
469
        int format;
 
470
 
 
471
#ifdef CONFIG_PUV3_PM
 
472
        struct clk *clk_vga;
 
473
        u32 pixclk = 0;
 
474
        int i;
 
475
 
 
476
        for (i = 0; i <= 10; i++) {
 
477
                if    (info->var.xres         == unifb_modes[i].xres
 
478
                    && info->var.yres         == unifb_modes[i].yres
 
479
                    && info->var.upper_margin == unifb_modes[i].upper_margin
 
480
                    && info->var.lower_margin == unifb_modes[i].lower_margin
 
481
                    && info->var.left_margin  == unifb_modes[i].left_margin
 
482
                    && info->var.right_margin == unifb_modes[i].right_margin
 
483
                    && info->var.hsync_len    == unifb_modes[i].hsync_len
 
484
                    && info->var.vsync_len    == unifb_modes[i].vsync_len) {
 
485
                        pixclk = unifb_modes[i].pixclock;
 
486
                        break;
 
487
                }
 
488
        }
 
489
 
 
490
        /* set clock rate */
 
491
        clk_vga = clk_get(info->device, "VGA_CLK");
 
492
        if (clk_vga == ERR_PTR(-ENOENT))
 
493
                return -ENOENT;
 
494
 
 
495
        if (pixclk != 0) {
 
496
                if (clk_set_rate(clk_vga, pixclk)) { /* set clock failed */
 
497
                        info->fix = unifb_fix;
 
498
                        info->var = unifb_default;
 
499
                        if (clk_set_rate(clk_vga, unifb_default.pixclock))
 
500
                                return -EINVAL;
 
501
                }
 
502
        }
 
503
#endif
 
504
 
 
505
        info->fix.line_length = get_line_length(info->var.xres_virtual,
 
506
                                                info->var.bits_per_pixel);
 
507
 
 
508
        hSyncStart = info->var.xres + info->var.right_margin;
 
509
        hSyncEnd = hSyncStart + info->var.hsync_len;
 
510
        hTotal = hSyncEnd + info->var.left_margin;
 
511
 
 
512
        vSyncStart = info->var.yres + info->var.lower_margin;
 
513
        vSyncEnd = vSyncStart + info->var.vsync_len;
 
514
        vTotal = vSyncEnd + info->var.upper_margin;
 
515
 
 
516
        switch (info->var.bits_per_pixel) {
 
517
        case 8:
 
518
                format = UDE_CFG_DST8;
 
519
                break;
 
520
        case 16:
 
521
                format = UDE_CFG_DST16;
 
522
                break;
 
523
        case 24:
 
524
                format = UDE_CFG_DST24;
 
525
                break;
 
526
        case 32:
 
527
                format = UDE_CFG_DST32;
 
528
                break;
 
529
        default:
 
530
                return -EINVAL;
 
531
        }
 
532
 
 
533
        writel(info->fix.smem_start, UDE_FSA);
 
534
        writel(info->var.yres, UDE_LS);
 
535
        writel(get_line_length(info->var.xres,
 
536
                        info->var.bits_per_pixel) >> 3, UDE_PS);
 
537
                        /* >> 3 for hardware required. */
 
538
        writel((hTotal << 16) | (info->var.xres), UDE_HAT);
 
539
        writel(((hTotal - 1) << 16) | (info->var.xres - 1), UDE_HBT);
 
540
        writel(((hSyncEnd - 1) << 16) | (hSyncStart - 1), UDE_HST);
 
541
        writel((vTotal << 16) | (info->var.yres), UDE_VAT);
 
542
        writel(((vTotal - 1) << 16) | (info->var.yres - 1), UDE_VBT);
 
543
        writel(((vSyncEnd - 1) << 16) | (vSyncStart - 1), UDE_VST);
 
544
        writel(UDE_CFG_GDEN_ENABLE | UDE_CFG_TIMEUP_ENABLE
 
545
                        | format | 0xC0000001, UDE_CFG);
 
546
 
 
547
        return 0;
 
548
}
 
549
 
 
550
/*
 
551
 *  Set a single color register. The values supplied are already
 
552
 *  rounded down to the hardware's capabilities (according to the
 
553
 *  entries in the var structure). Return != 0 for invalid regno.
 
554
 */
 
555
static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 
556
                         u_int transp, struct fb_info *info)
 
557
{
 
558
        if (regno >= 256)       /* no. of hw registers */
 
559
                return 1;
 
560
 
 
561
        /* grayscale works only partially under directcolor */
 
562
        if (info->var.grayscale) {
 
563
                /* grayscale = 0.30*R + 0.59*G + 0.11*B */
 
564
                red = green = blue =
 
565
                    (red * 77 + green * 151 + blue * 28) >> 8;
 
566
        }
 
567
 
 
568
#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
 
569
        switch (info->fix.visual) {
 
570
        case FB_VISUAL_TRUECOLOR:
 
571
        case FB_VISUAL_PSEUDOCOLOR:
 
572
                red = CNVT_TOHW(red, info->var.red.length);
 
573
                green = CNVT_TOHW(green, info->var.green.length);
 
574
                blue = CNVT_TOHW(blue, info->var.blue.length);
 
575
                transp = CNVT_TOHW(transp, info->var.transp.length);
 
576
                break;
 
577
        case FB_VISUAL_DIRECTCOLOR:
 
578
                red = CNVT_TOHW(red, 8);        /* expect 8 bit DAC */
 
579
                green = CNVT_TOHW(green, 8);
 
580
                blue = CNVT_TOHW(blue, 8);
 
581
                /* hey, there is bug in transp handling... */
 
582
                transp = CNVT_TOHW(transp, 8);
 
583
                break;
 
584
        }
 
585
#undef CNVT_TOHW
 
586
        /* Truecolor has hardware independent palette */
 
587
        if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
 
588
                u32 v;
 
589
 
 
590
                if (regno >= 16)
 
591
                        return 1;
 
592
 
 
593
                v = (red << info->var.red.offset) |
 
594
                    (green << info->var.green.offset) |
 
595
                    (blue << info->var.blue.offset) |
 
596
                    (transp << info->var.transp.offset);
 
597
                switch (info->var.bits_per_pixel) {
 
598
                case 8:
 
599
                        break;
 
600
                case 16:
 
601
                case 24:
 
602
                case 32:
 
603
                        ((u32 *) (info->pseudo_palette))[regno] = v;
 
604
                        break;
 
605
                default:
 
606
                        return 1;
 
607
                }
 
608
                return 0;
 
609
        }
 
610
        return 0;
 
611
}
 
612
 
 
613
/*
 
614
 *  Pan or Wrap the Display
 
615
 *
 
616
 *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
 
617
 */
 
618
static int unifb_pan_display(struct fb_var_screeninfo *var,
 
619
                           struct fb_info *info)
 
620
{
 
621
        if (var->vmode & FB_VMODE_YWRAP) {
 
622
                if (var->yoffset < 0
 
623
                    || var->yoffset >= info->var.yres_virtual
 
624
                    || var->xoffset)
 
625
                        return -EINVAL;
 
626
        } else {
 
627
                if (var->xoffset + var->xres > info->var.xres_virtual ||
 
628
                    var->yoffset + var->yres > info->var.yres_virtual)
 
629
                        return -EINVAL;
 
630
        }
 
631
        info->var.xoffset = var->xoffset;
 
632
        info->var.yoffset = var->yoffset;
 
633
        if (var->vmode & FB_VMODE_YWRAP)
 
634
                info->var.vmode |= FB_VMODE_YWRAP;
 
635
        else
 
636
                info->var.vmode &= ~FB_VMODE_YWRAP;
 
637
        return 0;
 
638
}
 
639
 
 
640
int unifb_mmap(struct fb_info *info,
 
641
                    struct vm_area_struct *vma)
 
642
{
 
643
        unsigned long size = vma->vm_end - vma->vm_start;
 
644
        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 
645
        unsigned long pos = info->fix.smem_start + offset;
 
646
 
 
647
        if (offset + size > info->fix.smem_len)
 
648
                return -EINVAL;
 
649
 
 
650
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
651
 
 
652
        if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size,
 
653
                                vma->vm_page_prot))
 
654
                return -EAGAIN;
 
655
 
 
656
        vma->vm_flags |= VM_RESERVED;   /* avoid to swap out this VMA */
 
657
        return 0;
 
658
 
 
659
}
 
660
 
 
661
static struct fb_ops unifb_ops = {
 
662
        .fb_read        = fb_sys_read,
 
663
        .fb_write       = fb_sys_write,
 
664
        .fb_check_var   = unifb_check_var,
 
665
        .fb_set_par     = unifb_set_par,
 
666
        .fb_setcolreg   = unifb_setcolreg,
 
667
        .fb_pan_display = unifb_pan_display,
 
668
        .fb_fillrect    = unifb_fillrect,
 
669
        .fb_copyarea    = unifb_copyarea,
 
670
        .fb_imageblit   = unifb_imageblit,
 
671
        .fb_mmap        = unifb_mmap,
 
672
};
 
673
 
 
674
/*
 
675
 *  Initialisation
 
676
 */
 
677
static int unifb_probe(struct platform_device *dev)
 
678
{
 
679
        struct fb_info *info;
 
680
        u32 unifb_regs[UNIFB_REGS_NUM];
 
681
        int retval = -ENOMEM;
 
682
        struct resource *iomem;
 
683
        void *videomemory;
 
684
 
 
685
        videomemory = (void *)__get_free_pages(GFP_KERNEL | __GFP_COMP,
 
686
                                get_order(UNIFB_MEMSIZE));
 
687
        if (!videomemory)
 
688
                goto err;
 
689
 
 
690
        memset(videomemory, 0, UNIFB_MEMSIZE);
 
691
 
 
692
        unifb_fix.smem_start = virt_to_phys(videomemory);
 
693
        unifb_fix.smem_len = UNIFB_MEMSIZE;
 
694
 
 
695
        iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
 
696
        unifb_fix.mmio_start = iomem->start;
 
697
 
 
698
        info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
 
699
        if (!info)
 
700
                goto err;
 
701
 
 
702
        info->screen_base = (char __iomem *)videomemory;
 
703
        info->fbops = &unifb_ops;
 
704
 
 
705
        retval = fb_find_mode(&info->var, info, NULL,
 
706
                              unifb_modes, 10, &unifb_modes[0], 16);
 
707
 
 
708
        if (!retval || (retval == 4))
 
709
                info->var = unifb_default;
 
710
 
 
711
        info->fix = unifb_fix;
 
712
        info->pseudo_palette = info->par;
 
713
        info->par = NULL;
 
714
        info->flags = FBINFO_FLAG_DEFAULT;
 
715
#ifdef FB_ACCEL_PUV3_UNIGFX
 
716
        info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
 
717
#endif
 
718
 
 
719
        retval = fb_alloc_cmap(&info->cmap, 256, 0);
 
720
        if (retval < 0)
 
721
                goto err1;
 
722
 
 
723
        retval = register_framebuffer(info);
 
724
        if (retval < 0)
 
725
                goto err2;
 
726
        platform_set_drvdata(dev, info);
 
727
        platform_device_add_data(dev, unifb_regs, sizeof(u32) * UNIFB_REGS_NUM);
 
728
 
 
729
        printk(KERN_INFO
 
730
               "fb%d: Virtual frame buffer device, using %dM of video memory\n",
 
731
               info->node, UNIFB_MEMSIZE >> 20);
 
732
        return 0;
 
733
err2:
 
734
        fb_dealloc_cmap(&info->cmap);
 
735
err1:
 
736
        framebuffer_release(info);
 
737
err:
 
738
        return retval;
 
739
}
 
740
 
 
741
static int unifb_remove(struct platform_device *dev)
 
742
{
 
743
        struct fb_info *info = platform_get_drvdata(dev);
 
744
 
 
745
        if (info) {
 
746
                unregister_framebuffer(info);
 
747
                fb_dealloc_cmap(&info->cmap);
 
748
                framebuffer_release(info);
 
749
        }
 
750
        return 0;
 
751
}
 
752
 
 
753
#ifdef CONFIG_PM
 
754
static int unifb_resume(struct platform_device *dev)
 
755
{
 
756
        int rc = 0;
 
757
        u32 *unifb_regs = dev->dev.platform_data;
 
758
 
 
759
        if (dev->dev.power.power_state.event == PM_EVENT_ON)
 
760
                return 0;
 
761
 
 
762
        console_lock();
 
763
 
 
764
        if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
 
765
                writel(unifb_regs[0], UDE_FSA);
 
766
                writel(unifb_regs[1], UDE_LS);
 
767
                writel(unifb_regs[2], UDE_PS);
 
768
                writel(unifb_regs[3], UDE_HAT);
 
769
                writel(unifb_regs[4], UDE_HBT);
 
770
                writel(unifb_regs[5], UDE_HST);
 
771
                writel(unifb_regs[6], UDE_VAT);
 
772
                writel(unifb_regs[7], UDE_VBT);
 
773
                writel(unifb_regs[8], UDE_VST);
 
774
                writel(unifb_regs[9], UDE_CFG);
 
775
        }
 
776
        dev->dev.power.power_state = PMSG_ON;
 
777
 
 
778
        console_unlock();
 
779
 
 
780
        return rc;
 
781
}
 
782
 
 
783
static int unifb_suspend(struct platform_device *dev, pm_message_t mesg)
 
784
{
 
785
        u32 *unifb_regs = dev->dev.platform_data;
 
786
 
 
787
        unifb_regs[0] = readl(UDE_FSA);
 
788
        unifb_regs[1] = readl(UDE_LS);
 
789
        unifb_regs[2] = readl(UDE_PS);
 
790
        unifb_regs[3] = readl(UDE_HAT);
 
791
        unifb_regs[4] = readl(UDE_HBT);
 
792
        unifb_regs[5] = readl(UDE_HST);
 
793
        unifb_regs[6] = readl(UDE_VAT);
 
794
        unifb_regs[7] = readl(UDE_VBT);
 
795
        unifb_regs[8] = readl(UDE_VST);
 
796
        unifb_regs[9] = readl(UDE_CFG);
 
797
 
 
798
        if (mesg.event == dev->dev.power.power_state.event)
 
799
                return 0;
 
800
 
 
801
        switch (mesg.event) {
 
802
        case PM_EVENT_FREEZE:           /* about to take snapshot */
 
803
        case PM_EVENT_PRETHAW:          /* before restoring snapshot */
 
804
                goto done;
 
805
        }
 
806
 
 
807
        console_lock();
 
808
 
 
809
        /* do nothing... */
 
810
 
 
811
        console_unlock();
 
812
 
 
813
done:
 
814
        dev->dev.power.power_state = mesg;
 
815
 
 
816
        return 0;
 
817
}
 
818
#else
 
819
#define unifb_resume    NULL
 
820
#define unifb_suspend   NULL
 
821
#endif
 
822
 
 
823
static struct platform_driver unifb_driver = {
 
824
        .probe   = unifb_probe,
 
825
        .remove  = unifb_remove,
 
826
        .resume  = unifb_resume,
 
827
        .suspend = unifb_suspend,
 
828
        .driver  = {
 
829
                .name   = "PKUnity-v3-UNIGFX",
 
830
        },
 
831
};
 
832
 
 
833
static int __init unifb_init(void)
 
834
{
 
835
#ifndef MODULE
 
836
        if (fb_get_options("unifb", NULL))
 
837
                return -ENODEV;
 
838
#endif
 
839
 
 
840
        return platform_driver_register(&unifb_driver);
 
841
}
 
842
 
 
843
module_init(unifb_init);
 
844
 
 
845
static void __exit unifb_exit(void)
 
846
{
 
847
        platform_driver_unregister(&unifb_driver);
 
848
}
 
849
 
 
850
module_exit(unifb_exit);
 
851
 
 
852
MODULE_LICENSE("GPL v2");