Merge master.kernel.org:/home/rmk/linux-2.6-mmc
[linux-2.6] / drivers / video / tx3912fb.c
1 /*
2  *  drivers/video/tx3912fb.c
3  *
4  *  Copyright (C) 1999 Harald Koerfgen
5  *  Copyright (C) 2001 Steven Hill (sjhill@realitydiluted.com)
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License. See the file COPYING in the main directory of this archive for
9  * more details.
10  *
11  *  Framebuffer for LCD controller in TMPR3912/05 and PR31700 processors
12  */
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
17 #include <linux/tty.h>
18 #include <linux/delay.h>
19 #include <linux/interrupt.h>
20 #include <linux/init.h>
21 #include <linux/pm.h>
22 #include <linux/fb.h>
23 #include <asm/io.h>
24 #include <asm/bootinfo.h>
25 #include <asm/uaccess.h>
26 #include <asm/tx3912.h>
27 #include <video/tx3912.h>
28
29 /*
30  * Frame buffer, palette and console structures
31  */
32 static struct fb_info fb_info;
33 static u32 cfb8[16];
34
35 static struct fb_fix_screeninfo tx3912fb_fix __initdata = {
36         .id =           "tx3912fb",
37         .smem_len =     ((240 * 320)/2),
38         .type =         FB_TYPE_PACKED_PIXELS,
39         .visual =       FB_VISUAL_TRUECOLOR, 
40         .xpanstep =     1,
41         .ypanstep =     1,
42         .ywrapstep =    1,
43         .accel =        FB_ACCEL_NONE,
44 };
45
46 static struct fb_var_screeninfo tx3912fb_var = {
47         .xres =         240,
48         .yres =         320,
49         .xres_virtual = 240,
50         .yres_virtual = 320,
51         .bits_per_pixel =4,
52         .red =          { 0, 4, 0 },    /* ??? */
53         .green =        { 0, 4, 0 },
54         .blue =         { 0, 4, 0 },
55         .activate =     FB_ACTIVATE_NOW,
56         .width =        -1,
57         .height =       -1,
58         .pixclock =     20000,
59         .left_margin =  64,
60         .right_margin = 64,
61         .upper_margin = 32,
62         .lower_margin = 32,
63         .hsync_len =    64,
64         .vsync_len =    2,
65         .vmode =        FB_VMODE_NONINTERLACED,
66 };
67
68 /*
69  * Interface used by the world
70  */
71 int tx3912fb_init(void);
72
73 static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green,
74                               u_int blue, u_int transp,
75                               struct fb_info *info);
76
77 /*
78  * Macros
79  */
80 #define get_line_length(xres_virtual, bpp) \
81                 (u_long) (((int) xres_virtual * (int) bpp + 7) >> 3)
82
83 /*
84  * Frame buffer operations structure used by console driver
85  */
86 static struct fb_ops tx3912fb_ops = {
87         .owner          = THIS_MODULE,
88         .fb_setcolreg   = tx3912fb_setcolreg,
89         .fb_fillrect    = cfb_fillrect,
90         .fb_copyarea    = cfb_copyarea,
91         .fb_imageblit   = cfb_imageblit,
92 };
93
94 static int tx3912fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
95 {
96         /*
97          * Memory limit
98          */
99         line_length =
100             get_line_length(var->xres_virtual, var->bits_per_pixel);
101         if ((line_length * var->yres_virtual) > info->fix.smem_len)
102                 return -ENOMEM;
103
104         return 0;
105 }
106
107 static int tx3912fb_set_par(struct fb_info *info)
108 {
109         u_long tx3912fb_paddr = 0;
110
111         /* Disable the video logic */
112         outl(inl(TX3912_VIDEO_CTRL1) &
113              ~(TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
114              TX3912_VIDEO_CTRL1);
115         udelay(200);
116
117         /* Set start address for DMA transfer */
118         outl(tx3912fb_paddr, TX3912_VIDEO_CTRL3);
119
120         /* Set end address for DMA transfer */
121         outl((tx3912fb_paddr + tx3912fb_fix.smem_len + 1), TX3912_VIDEO_CTRL4);
122
123         /* Set the pixel depth */
124         switch (info->var.bits_per_pixel) {
125         case 1:
126                 /* Monochrome */
127                 outl(inl(TX3912_VIDEO_CTRL1) &
128                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
129                 info->fix.visual = FB_VISUAL_MONO10;
130                 break;
131         case 4:
132                 /* 4-bit gray */
133                 outl(inl(TX3912_VIDEO_CTRL1) &
134                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
135                 outl(inl(TX3912_VIDEO_CTRL1) |
136                      TX3912_VIDEO_CTRL1_BITSEL_4BIT_GRAY,
137                      TX3912_VIDEO_CTRL1);
138                 info->fix.visual = FB_VISUAL_TRUECOLOR;
139                 break;
140         case 8:
141                 /* 8-bit color */
142                 outl(inl(TX3912_VIDEO_CTRL1) &
143                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
144                 outl(inl(TX3912_VIDEO_CTRL1) |
145                      TX3912_VIDEO_CTRL1_BITSEL_8BIT_COLOR,
146                      TX3912_VIDEO_CTRL1);
147                 info->fix.visual = FB_VISUAL_TRUECOLOR;
148                 break;
149         case 2:
150         default:
151                 /* 2-bit gray */
152                 outl(inl(TX3912_VIDEO_CTRL1) &
153                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
154                 outl(inl(TX3912_VIDEO_CTRL1) |
155                      TX3912_VIDEO_CTRL1_BITSEL_2BIT_GRAY,
156                      TX3912_VIDEO_CTRL1);
157                 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
158                 break;
159         }
160
161         /* Enable the video clock */
162         outl(inl(TX3912_CLK_CTRL) | TX3912_CLK_CTRL_ENVIDCLK,
163              TX3912_CLK_CTRL);
164
165         /* Unfreeze video logic and enable DF toggle */
166         outl(inl(TX3912_VIDEO_CTRL1) &
167              ~(TX3912_VIDEO_CTRL1_ENFREEZEFRAME |
168                TX3912_VIDEO_CTRL1_DFMODE)
169              , TX3912_VIDEO_CTRL1);
170         udelay(200);
171
172         /* Enable the video logic */
173         outl(inl(TX3912_VIDEO_CTRL1) |
174              (TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
175              TX3912_VIDEO_CTRL1);
176
177         info->fix.line_length = get_line_length(var->xres_virtual,
178                                             var->bits_per_pixel);
179 }
180
181 /*
182  * Set a single color register
183  */
184 static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green,
185                               u_int blue, u_int transp,
186                               struct fb_info *info)
187 {
188         if (regno > 255)
189                 return 1;
190
191         if (regno < 16)
192                 ((u32 *)(info->pseudo_palette))[regno] = ((red & 0xe000) >> 8)
193                     | ((green & 0xe000) >> 11)
194                     | ((blue & 0xc000) >> 14);
195         return 0;
196 }
197
198 int __init tx3912fb_setup(char *options);
199
200 /*
201  * Initialization of the framebuffer
202  */
203 int __init tx3912fb_init(void)
204 {
205         u_long tx3912fb_paddr = 0;
206         int size = (info->var.bits_per_pixel == 8) ? 256 : 16;
207         char *option = NULL;
208
209         if (fb_get_options("tx3912fb", &option))
210                 return -ENODEV;
211         tx3912fb_setup(option);
212
213         /* Disable the video logic */
214         outl(inl(TX3912_VIDEO_CTRL1) &
215              ~(TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
216              TX3912_VIDEO_CTRL1);
217         udelay(200);
218
219         /* Set start address for DMA transfer */
220         outl(tx3912fb_paddr, TX3912_VIDEO_CTRL3);
221
222         /* Set end address for DMA transfer */
223         outl((tx3912fb_paddr + tx3912fb_fix.smem_len + 1), TX3912_VIDEO_CTRL4);
224
225         /* Set the pixel depth */
226         switch (tx3912fb_var.bits_per_pixel) {
227         case 1:
228                 /* Monochrome */
229                 outl(inl(TX3912_VIDEO_CTRL1) &
230                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
231                 tx3912fb_fix.visual = FB_VISUAL_MONO10;
232                 break;
233         case 4:
234                 /* 4-bit gray */
235                 outl(inl(TX3912_VIDEO_CTRL1) &
236                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
237                 outl(inl(TX3912_VIDEO_CTRL1) |
238                      TX3912_VIDEO_CTRL1_BITSEL_4BIT_GRAY,
239                      TX3912_VIDEO_CTRL1);
240                 tx3912fb_fix.visual = FB_VISUAL_TRUECOLOR;
241                 tx3912fb_fix.grayscale = 1;
242                 break;
243         case 8:
244                 /* 8-bit color */
245                 outl(inl(TX3912_VIDEO_CTRL1) &
246                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
247                 outl(inl(TX3912_VIDEO_CTRL1) |
248                      TX3912_VIDEO_CTRL1_BITSEL_8BIT_COLOR,
249                      TX3912_VIDEO_CTRL1);
250                 tx3912fb_fix.visual = FB_VISUAL_TRUECOLOR;
251                 break;
252         case 2:
253         default:
254                 /* 2-bit gray */
255                 outl(inl(TX3912_VIDEO_CTRL1) &
256                      ~TX3912_VIDEO_CTRL1_BITSEL_MASK, TX3912_VIDEO_CTRL1);
257                 outl(inl(TX3912_VIDEO_CTRL1) |
258                      TX3912_VIDEO_CTRL1_BITSEL_2BIT_GRAY,
259                      TX3912_VIDEO_CTRL1);
260                 tx3912fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;
261                 tx3912fb_fix.grayscale = 1;
262                 break;
263         }
264
265         /* Enable the video clock */
266         outl(inl(TX3912_CLK_CTRL) | TX3912_CLK_CTRL_ENVIDCLK,
267                 TX3912_CLK_CTRL);
268
269         /* Unfreeze video logic and enable DF toggle */
270         outl(inl(TX3912_VIDEO_CTRL1) &
271                 ~(TX3912_VIDEO_CTRL1_ENFREEZEFRAME | TX3912_VIDEO_CTRL1_DFMODE),
272                 TX3912_VIDEO_CTRL1);
273         udelay(200);
274
275         /* Clear the framebuffer */
276         memset((void *) tx3912fb_fix.smem_start, 0xff, tx3912fb_fix.smem_len);
277         udelay(200);
278
279         /* Enable the video logic */
280         outl(inl(TX3912_VIDEO_CTRL1) |
281                 (TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
282                 TX3912_VIDEO_CTRL1);
283
284         /*
285          * Memory limit
286          */
287         tx3912fb_fix.line_length =
288             get_line_length(tx3912fb_var.xres_virtual, tx3912fb_var.bits_per_pixel);
289         if ((tx3912fb_fix.line_length * tx3912fb_var.yres_virtual) > tx3912fb_fix.smem_len)
290                 return -ENOMEM;
291
292         fb_info.fbops = &tx3912fb_ops;
293         fb_info.var = tx3912fb_var;
294         fb_info.fix = tx3912fb_fix;
295         fb_info.pseudo_palette = pseudo_palette;
296         fb_info.flags = FBINFO_DEFAULT;
297
298         /* Clear the framebuffer */
299         memset((void *) fb_info.fix.smem_start, 0xff, fb_info.fix.smem_len);
300         udelay(200);
301
302         fb_alloc_cmap(&info->cmap, size, 0);
303
304         if (register_framebuffer(&fb_info) < 0)
305                 return -1;
306
307         printk(KERN_INFO "fb%d: TX3912 frame buffer using %uKB.\n",
308                fb_info.node, (u_int) (fb_info.fix.smem_len >> 10));
309         return 0;
310 }
311
312 int __init tx3912fb_setup(char *options)
313 {
314         char *this_opt;
315
316         if (!options || !*options)
317                 return 0;
318
319         while ((this_opt = strsep(&options, ","))) {
320                 if (!strncmp(options, "bpp:", 4))       
321                         tx3912fb_var.bits_per_pixel = simple_strtoul(options+4, NULL, 0);
322         }       
323         return 0;
324 }
325
326 module_init(tx3912fb_init);
327 MODULE_LICENSE("GPL");