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