Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
[linux-2.6] / drivers / video / pmag-ba-fb.c
1 /*
2  *      linux/drivers/video/pmag-ba-fb.c
3  *
4  *      PMAG-BA TURBOchannel Color Frame Buffer (CFB) card support,
5  *      derived from:
6  *      "HP300 Topcat framebuffer support (derived from macfb of all things)
7  *      Phil Blundell <philb@gnu.org> 1998", the original code can be
8  *      found in the file hpfb.c in the same directory.
9  *
10  *      Based on digital document:
11  *      "PMAG-BA TURBOchannel Color Frame Buffer
12  *       Functional Specification", Revision 1.2, August 27, 1990
13  *
14  *      DECstation related code Copyright (C) 1999, 2000, 2001 by
15  *      Michael Engel <engel@unix-ag.org>,
16  *      Karsten Merker <merker@linuxtag.org> and
17  *      Harald Koerfgen.
18  *      Copyright (c) 2005  Maciej W. Rozycki
19  *
20  *      This file is subject to the terms and conditions of the GNU General
21  *      Public License.  See the file COPYING in the main directory of this
22  *      archive for more details.
23  */
24
25 #include <linux/compiler.h>
26 #include <linux/errno.h>
27 #include <linux/fb.h>
28 #include <linux/init.h>
29 #include <linux/kernel.h>
30 #include <linux/module.h>
31 #include <linux/types.h>
32
33 #include <asm/io.h>
34 #include <asm/system.h>
35
36 #include <asm/dec/tc.h>
37
38 #include <video/pmag-ba-fb.h>
39
40
41 struct pmagbafb_par {
42         struct fb_info *next;
43         volatile void __iomem *mmio;
44         volatile u32 __iomem *dac;
45         int slot;
46 };
47
48
49 static struct fb_info *root_pmagbafb_dev;
50
51 static struct fb_var_screeninfo pmagbafb_defined __initdata = {
52         .xres           = 1024,
53         .yres           = 864,
54         .xres_virtual   = 1024,
55         .yres_virtual   = 864,
56         .bits_per_pixel = 8,
57         .red.length     = 8,
58         .green.length   = 8,
59         .blue.length    = 8,
60         .activate       = FB_ACTIVATE_NOW,
61         .height         = -1,
62         .width          = -1,
63         .accel_flags    = FB_ACCEL_NONE,
64         .pixclock       = 14452,
65         .left_margin    = 116,
66         .right_margin   = 12,
67         .upper_margin   = 34,
68         .lower_margin   = 12,
69         .hsync_len      = 128,
70         .vsync_len      = 3,
71         .sync           = FB_SYNC_ON_GREEN,
72         .vmode          = FB_VMODE_NONINTERLACED,
73 };
74
75 static struct fb_fix_screeninfo pmagbafb_fix __initdata = {
76         .id             = "PMAG-BA",
77         .smem_len       = (1024 * 1024),
78         .type           = FB_TYPE_PACKED_PIXELS,
79         .visual         = FB_VISUAL_PSEUDOCOLOR,
80         .line_length    = 1024,
81         .mmio_len       = PMAG_BA_SIZE - PMAG_BA_BT459,
82 };
83
84
85 static inline void dac_write(struct pmagbafb_par *par, unsigned int reg, u8 v)
86 {
87         writeb(v, par->dac + reg / 4);
88 }
89
90 static inline u8 dac_read(struct pmagbafb_par *par, unsigned int reg)
91 {
92         return readb(par->dac + reg / 4);
93 }
94
95
96 /*
97  * Set the palette.
98  */
99 static int pmagbafb_setcolreg(unsigned int regno, unsigned int red,
100                               unsigned int green, unsigned int blue,
101                               unsigned int transp, struct fb_info *info)
102 {
103         struct pmagbafb_par *par = info->par;
104
105         BUG_ON(regno >= info->cmap.len);
106
107         red   >>= 8;    /* The cmap fields are 16 bits    */
108         green >>= 8;    /* wide, but the hardware colormap */
109         blue  >>= 8;    /* registers are only 8 bits wide */
110
111         mb();
112         dac_write(par, BT459_ADDR_LO, regno);
113         dac_write(par, BT459_ADDR_HI, 0x00);
114         wmb();
115         dac_write(par, BT459_CMAP, red);
116         wmb();
117         dac_write(par, BT459_CMAP, green);
118         wmb();
119         dac_write(par, BT459_CMAP, blue);
120
121         return 0;
122 }
123
124 static struct fb_ops pmagbafb_ops = {
125         .owner          = THIS_MODULE,
126         .fb_setcolreg   = pmagbafb_setcolreg,
127         .fb_fillrect    = cfb_fillrect,
128         .fb_copyarea    = cfb_copyarea,
129         .fb_imageblit   = cfb_imageblit,
130 };
131
132
133 /*
134  * Turn the hardware cursor off.
135  */
136 static void __init pmagbafb_erase_cursor(struct fb_info *info)
137 {
138         struct pmagbafb_par *par = info->par;
139
140         mb();
141         dac_write(par, BT459_ADDR_LO, 0x00);
142         dac_write(par, BT459_ADDR_HI, 0x03);
143         wmb();
144         dac_write(par, BT459_DATA, 0x00);
145 }
146
147
148 static int __init pmagbafb_init_one(int slot)
149 {
150         struct fb_info *info;
151         struct pmagbafb_par *par;
152         unsigned long base_addr;
153
154         info = framebuffer_alloc(sizeof(struct pmagbafb_par), NULL);
155         if (!info)
156                 return -ENOMEM;
157
158         par = info->par;
159         par->slot = slot;
160         claim_tc_card(par->slot);
161
162         base_addr = get_tc_base_addr(par->slot);
163
164         par->next = root_pmagbafb_dev;
165         root_pmagbafb_dev = info;
166
167         if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
168                 goto err_alloc;
169
170         info->fbops = &pmagbafb_ops;
171         info->fix = pmagbafb_fix;
172         info->var = pmagbafb_defined;
173         info->flags = FBINFO_DEFAULT;
174
175         /* MMIO mapping setup.  */
176         info->fix.mmio_start = base_addr;
177         par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
178         if (!par->mmio)
179                 goto err_cmap;
180         par->dac = par->mmio + PMAG_BA_BT459;
181
182         /* Frame buffer mapping setup.  */
183         info->fix.smem_start = base_addr + PMAG_BA_FBMEM;
184         info->screen_base = ioremap_nocache(info->fix.smem_start,
185                                             info->fix.smem_len);
186         if (!info->screen_base)
187                 goto err_mmio_map;
188         info->screen_size = info->fix.smem_len;
189
190         pmagbafb_erase_cursor(info);
191
192         if (register_framebuffer(info) < 0)
193                 goto err_smem_map;
194
195         pr_info("fb%d: %s frame buffer device in slot %d\n",
196                 info->node, info->fix.id, par->slot);
197
198         return 0;
199
200
201 err_smem_map:
202         iounmap(info->screen_base);
203
204 err_mmio_map:
205         iounmap(par->mmio);
206
207 err_cmap:
208         fb_dealloc_cmap(&info->cmap);
209
210 err_alloc:
211         root_pmagbafb_dev = par->next;
212         release_tc_card(par->slot);
213         framebuffer_release(info);
214         return -ENXIO;
215 }
216
217 static void __exit pmagbafb_exit_one(void)
218 {
219         struct fb_info *info = root_pmagbafb_dev;
220         struct pmagbafb_par *par = info->par;
221
222         unregister_framebuffer(info);
223         iounmap(info->screen_base);
224         iounmap(par->mmio);
225         fb_dealloc_cmap(&info->cmap);
226         root_pmagbafb_dev = par->next;
227         release_tc_card(par->slot);
228         framebuffer_release(info);
229 }
230
231
232 /*
233  * Initialise the framebuffer.
234  */
235 static int __init pmagbafb_init(void)
236 {
237         int count = 0;
238         int slot;
239
240         if (fb_get_options("pmagbafb", NULL))
241                 return -ENXIO;
242
243         while ((slot = search_tc_card("PMAG-BA")) >= 0) {
244                 if (pmagbafb_init_one(slot) < 0)
245                         break;
246                 count++;
247         }
248         return (count > 0) ? 0 : -ENXIO;
249 }
250
251 static void __exit pmagbafb_exit(void)
252 {
253         while (root_pmagbafb_dev)
254                 pmagbafb_exit_one();
255 }
256
257
258 module_init(pmagbafb_init);
259 module_exit(pmagbafb_exit);
260
261 MODULE_LICENSE("GPL");