Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/video/pmag-aa-fb.c | |
3 | * Copyright 2002 Karsten Merker <merker@debian.org> | |
4 | * | |
5 | * PMAG-AA TurboChannel framebuffer card support ... derived from | |
6 | * pmag-ba-fb.c, which is Copyright (C) 1999, 2000, 2001 by | |
7 | * Michael Engel <engel@unix-ag.org>, Karsten Merker <merker@debian.org> | |
8 | * and Harald Koerfgen <hkoerfg@web.de>, which itself is derived from | |
9 | * "HP300 Topcat framebuffer support (derived from macfb of all things) | |
10 | * Phil Blundell <philb@gnu.org> 1998" | |
90c83176 | 11 | * Copyright (c) 2016 Maciej W. Rozycki |
1da177e4 LT |
12 | * |
13 | * This file is subject to the terms and conditions of the GNU General | |
14 | * Public License. See the file COPYING in the main directory of this | |
15 | * archive for more details. | |
16 | * | |
17 | * 2002-09-28 Karsten Merker <merker@linuxtag.org> | |
18 | * Version 0.01: First try to get a PMAG-AA running. | |
19 | * | |
20 | * 2003-02-24 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> | |
21 | * Version 0.02: Major code cleanup. | |
22 | * | |
23 | * 2003-09-21 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> | |
24 | * Hardware cursor support. | |
90c83176 MR |
25 | * |
26 | * 2016-02-21 Maciej W. Rozycki <macro@linux-mips.org> | |
27 | * Version 0.03: Rewritten for the new FB and TC APIs. | |
1da177e4 | 28 | */ |
90c83176 MR |
29 | |
30 | #include <linux/compiler.h> | |
1da177e4 | 31 | #include <linux/errno.h> |
1da177e4 | 32 | #include <linux/fb.h> |
90c83176 MR |
33 | #include <linux/init.h> |
34 | #include <linux/io.h> | |
35 | #include <linux/kernel.h> | |
36 | #include <linux/module.h> | |
37 | #include <linux/tc.h> | |
38 | #include <linux/timer.h> | |
1da177e4 LT |
39 | |
40 | #include "bt455.h" | |
41 | #include "bt431.h" | |
42 | ||
43 | /* Version information */ | |
90c83176 | 44 | #define DRIVER_VERSION "0.03" |
1da177e4 LT |
45 | #define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>" |
46 | #define DRIVER_DESCRIPTION "PMAG-AA Framebuffer Driver" | |
47 | ||
1da177e4 LT |
48 | /* |
49 | * Bt455 RAM DAC register base offset (rel. to TC slot base address). | |
50 | */ | |
51 | #define PMAG_AA_BT455_OFFSET 0x100000 | |
52 | ||
53 | /* | |
54 | * Bt431 cursor generator offset (rel. to TC slot base address). | |
55 | */ | |
56 | #define PMAG_AA_BT431_OFFSET 0x180000 | |
57 | ||
58 | /* | |
59 | * Begin of PMAG-AA framebuffer memory relative to TC slot address, | |
60 | * resolution is 1280x1024x1 (8 bits deep, but only LSB is used). | |
61 | */ | |
62 | #define PMAG_AA_ONBOARD_FBMEM_OFFSET 0x200000 | |
63 | ||
90c83176 MR |
64 | struct aafb_par { |
65 | void __iomem *mmio; | |
66 | struct bt455_regs __iomem *bt455; | |
67 | struct bt431_regs __iomem *bt431; | |
1da177e4 LT |
68 | }; |
69 | ||
90c83176 MR |
70 | static struct fb_var_screeninfo aafb_defined = { |
71 | .xres = 1280, | |
72 | .yres = 1024, | |
73 | .xres_virtual = 2048, | |
74 | .yres_virtual = 1024, | |
75 | .bits_per_pixel = 8, | |
76 | .grayscale = 1, | |
77 | .red.length = 0, | |
78 | .green.length = 1, | |
79 | .blue.length = 0, | |
80 | .activate = FB_ACTIVATE_NOW, | |
81 | .accel_flags = FB_ACCEL_NONE, | |
df082104 MR |
82 | .pixclock = 7645, |
83 | .left_margin = 224, | |
84 | .right_margin = 32, | |
85 | .upper_margin = 33, | |
86 | .lower_margin = 3, | |
87 | .hsync_len = 160, | |
88 | .vsync_len = 3, | |
90c83176 MR |
89 | .sync = FB_SYNC_ON_GREEN, |
90 | .vmode = FB_VMODE_NONINTERLACED, | |
1da177e4 LT |
91 | }; |
92 | ||
90c83176 MR |
93 | static struct fb_fix_screeninfo aafb_fix = { |
94 | .id = "PMAG-AA", | |
95 | .smem_len = (2048 * 1024), | |
96 | .type = FB_TYPE_PACKED_PIXELS, | |
97 | .visual = FB_VISUAL_MONO10, | |
98 | .ypanstep = 1, | |
99 | .ywrapstep = 1, | |
100 | .line_length = 2048, | |
101 | .mmio_len = PMAG_AA_ONBOARD_FBMEM_OFFSET - PMAG_AA_BT455_OFFSET, | |
102 | }; | |
1da177e4 | 103 | |
90c83176 | 104 | static int aafb_cursor(struct fb_info *info, struct fb_cursor *cursor) |
1da177e4 | 105 | { |
90c83176 | 106 | struct aafb_par *par = info->par; |
1da177e4 | 107 | |
90c83176 MR |
108 | if (cursor->image.height > BT431_CURSOR_SIZE || |
109 | cursor->image.width > BT431_CURSOR_SIZE) { | |
110 | bt431_erase_cursor(par->bt431); | |
1da177e4 | 111 | return -EINVAL; |
1da177e4 LT |
112 | } |
113 | ||
90c83176 MR |
114 | if (!cursor->enable) |
115 | bt431_erase_cursor(par->bt431); | |
1da177e4 | 116 | |
90c83176 MR |
117 | if (cursor->set & FB_CUR_SETPOS) |
118 | bt431_position_cursor(par->bt431, | |
119 | cursor->image.dx, cursor->image.dy); | |
120 | if (cursor->set & FB_CUR_SETCMAP) { | |
121 | u8 fg = cursor->image.fg_color ? 0xf : 0x0; | |
122 | u8 bg = cursor->image.bg_color ? 0xf : 0x0; | |
1da177e4 | 123 | |
01ac59c3 | 124 | bt455_write_cmap_entry(par->bt455, 8, bg); |
5832706e MR |
125 | bt455_write_cmap_next(par->bt455, bg); |
126 | bt455_write_ovly_next(par->bt455, fg); | |
90c83176 MR |
127 | } |
128 | if (cursor->set & (FB_CUR_SETSIZE | FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) | |
129 | bt431_set_cursor(par->bt431, | |
130 | cursor->image.data, cursor->mask, cursor->rop, | |
131 | cursor->image.width, cursor->image.height); | |
1da177e4 | 132 | |
90c83176 MR |
133 | if (cursor->enable) |
134 | bt431_enable_cursor(par->bt431); | |
1da177e4 | 135 | |
1da177e4 LT |
136 | return 0; |
137 | } | |
138 | ||
90c83176 | 139 | /* 0 unblanks, any other blanks. */ |
1da177e4 | 140 | |
90c83176 | 141 | static int aafb_blank(int blank, struct fb_info *info) |
1da177e4 | 142 | { |
90c83176 MR |
143 | struct aafb_par *par = info->par; |
144 | u8 val = blank ? 0x00 : 0x0f; | |
1da177e4 | 145 | |
01ac59c3 | 146 | bt455_write_cmap_entry(par->bt455, 1, val); |
1da177e4 LT |
147 | return 0; |
148 | } | |
149 | ||
90c83176 MR |
150 | static struct fb_ops aafb_ops = { |
151 | .owner = THIS_MODULE, | |
152 | .fb_blank = aafb_blank, | |
153 | .fb_fillrect = cfb_fillrect, | |
154 | .fb_copyarea = cfb_copyarea, | |
155 | .fb_imageblit = cfb_imageblit, | |
156 | .fb_cursor = aafb_cursor, | |
157 | }; | |
1da177e4 | 158 | |
90c83176 | 159 | static int pmagaafb_probe(struct device *dev) |
1da177e4 | 160 | { |
90c83176 MR |
161 | struct tc_dev *tdev = to_tc_dev(dev); |
162 | resource_size_t start, len; | |
163 | struct fb_info *info; | |
164 | struct aafb_par *par; | |
165 | int err; | |
166 | ||
167 | info = framebuffer_alloc(sizeof(struct aafb_par), dev); | |
168 | if (!info) { | |
169 | printk(KERN_ERR "%s: Cannot allocate memory\n", dev_name(dev)); | |
170 | return -ENOMEM; | |
171 | } | |
1da177e4 | 172 | |
90c83176 MR |
173 | par = info->par; |
174 | dev_set_drvdata(dev, info); | |
175 | ||
176 | info->fbops = &aafb_ops; | |
177 | info->fix = aafb_fix; | |
178 | info->var = aafb_defined; | |
179 | info->flags = FBINFO_DEFAULT; | |
180 | ||
181 | /* Request the I/O MEM resource. */ | |
182 | start = tdev->resource.start; | |
183 | len = tdev->resource.end - start + 1; | |
184 | if (!request_mem_region(start, len, dev_name(dev))) { | |
185 | printk(KERN_ERR "%s: Cannot reserve FB region\n", | |
186 | dev_name(dev)); | |
187 | err = -EBUSY; | |
188 | goto err_alloc; | |
189 | } | |
1da177e4 | 190 | |
90c83176 MR |
191 | /* MMIO mapping setup. */ |
192 | info->fix.mmio_start = start + PMAG_AA_BT455_OFFSET; | |
193 | par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len); | |
194 | if (!par->mmio) { | |
195 | printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev)); | |
196 | err = -ENOMEM; | |
197 | goto err_resource; | |
198 | } | |
199 | par->bt455 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT455_OFFSET; | |
200 | par->bt431 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT431_OFFSET; | |
201 | ||
202 | /* Frame buffer mapping setup. */ | |
203 | info->fix.smem_start = start + PMAG_AA_ONBOARD_FBMEM_OFFSET; | |
204 | info->screen_base = ioremap_nocache(info->fix.smem_start, | |
205 | info->fix.smem_len); | |
206 | if (!info->screen_base) { | |
207 | printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev)); | |
208 | err = -ENOMEM; | |
209 | goto err_mmio_map; | |
210 | } | |
211 | info->screen_size = info->fix.smem_len; | |
1da177e4 | 212 | |
90c83176 | 213 | /* Init colormap. */ |
01ac59c3 | 214 | bt455_write_cmap_entry(par->bt455, 0, 0x0); |
5832706e | 215 | bt455_write_cmap_next(par->bt455, 0xf); |
1da177e4 | 216 | |
90c83176 MR |
217 | /* Init hardware cursor. */ |
218 | bt431_erase_cursor(par->bt431); | |
219 | bt431_init_cursor(par->bt431); | |
220 | ||
221 | err = register_framebuffer(info); | |
222 | if (err < 0) { | |
223 | printk(KERN_ERR "%s: Cannot register framebuffer\n", | |
224 | dev_name(dev)); | |
225 | goto err_smem_map; | |
226 | } | |
1da177e4 | 227 | |
90c83176 | 228 | get_device(dev); |
1da177e4 | 229 | |
90c83176 MR |
230 | pr_info("fb%d: %s frame buffer device at %s\n", |
231 | info->node, info->fix.id, dev_name(dev)); | |
1da177e4 LT |
232 | |
233 | return 0; | |
1da177e4 | 234 | |
1da177e4 | 235 | |
90c83176 MR |
236 | err_smem_map: |
237 | iounmap(info->screen_base); | |
1da177e4 | 238 | |
90c83176 MR |
239 | err_mmio_map: |
240 | iounmap(par->mmio); | |
1da177e4 | 241 | |
90c83176 MR |
242 | err_resource: |
243 | release_mem_region(start, len); | |
1da177e4 | 244 | |
90c83176 MR |
245 | err_alloc: |
246 | framebuffer_release(info); | |
247 | return err; | |
1da177e4 LT |
248 | } |
249 | ||
90c83176 | 250 | static int __exit pmagaafb_remove(struct device *dev) |
1da177e4 | 251 | { |
90c83176 MR |
252 | struct tc_dev *tdev = to_tc_dev(dev); |
253 | struct fb_info *info = dev_get_drvdata(dev); | |
254 | struct aafb_par *par = info->par; | |
255 | resource_size_t start, len; | |
256 | ||
257 | put_device(dev); | |
258 | unregister_framebuffer(info); | |
259 | iounmap(info->screen_base); | |
260 | iounmap(par->mmio); | |
261 | start = tdev->resource.start; | |
262 | len = tdev->resource.end - start + 1; | |
263 | release_mem_region(start, len); | |
264 | framebuffer_release(info); | |
1da177e4 LT |
265 | return 0; |
266 | } | |
267 | ||
268 | /* | |
269 | * Initialise the framebuffer. | |
270 | */ | |
90c83176 MR |
271 | static const struct tc_device_id pmagaafb_tc_table[] = { |
272 | { "DEC ", "PMAG-AA " }, | |
273 | { } | |
274 | }; | |
275 | MODULE_DEVICE_TABLE(tc, pmagaafb_tc_table); | |
276 | ||
277 | static struct tc_driver pmagaafb_driver = { | |
278 | .id_table = pmagaafb_tc_table, | |
279 | .driver = { | |
280 | .name = "pmagaafb", | |
281 | .bus = &tc_bus_type, | |
282 | .probe = pmagaafb_probe, | |
283 | .remove = __exit_p(pmagaafb_remove), | |
284 | }, | |
285 | }; | |
1da177e4 | 286 | |
90c83176 MR |
287 | static int __init pmagaafb_init(void) |
288 | { | |
289 | #ifndef MODULE | |
290 | if (fb_get_options("pmagaafb", NULL)) | |
291 | return -ENXIO; | |
292 | #endif | |
293 | return tc_register_driver(&pmagaafb_driver); | |
1da177e4 LT |
294 | } |
295 | ||
296 | static void __exit pmagaafb_exit(void) | |
297 | { | |
90c83176 | 298 | tc_unregister_driver(&pmagaafb_driver); |
1da177e4 LT |
299 | } |
300 | ||
90c83176 MR |
301 | module_init(pmagaafb_init); |
302 | module_exit(pmagaafb_exit); | |
303 | ||
1da177e4 LT |
304 | MODULE_AUTHOR(DRIVER_AUTHOR); |
305 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); | |
306 | MODULE_LICENSE("GPL"); |