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" |
11 | * Copyright (c) 2016 Maciej W. Rozycki |
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. |
25 | * |
26 | * 2016-02-21 Maciej W. Rozycki <macro@linux-mips.org> |
27 | * Version 0.03: Rewritten for the new FB and TC APIs. |
28 | */ |
29 | |
30 | #include <linux/compiler.h> |
31 | #include <linux/errno.h> |
32 | #include <linux/fb.h> |
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> |
39 | |
40 | #include "bt455.h" |
41 | #include "bt431.h" |
42 | |
43 | /* Version information */ |
44 | #define DRIVER_VERSION "0.03" |
45 | #define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>" |
46 | #define DRIVER_DESCRIPTION "PMAG-AA Framebuffer Driver" |
47 | |
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 | |
64 | struct aafb_par { |
65 | void __iomem *mmio; |
66 | struct bt455_regs __iomem *bt455; |
67 | struct bt431_regs __iomem *bt431; |
68 | }; |
69 | |
70 | static const 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, |
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, |
89 | .sync = FB_SYNC_ON_GREEN, |
90 | .vmode = FB_VMODE_NONINTERLACED, |
91 | }; |
92 | |
93 | static const 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 | }; |
103 | |
104 | static int aafb_cursor(struct fb_info *info, struct fb_cursor *cursor) |
105 | { |
106 | struct aafb_par *par = info->par; |
107 | |
108 | if (cursor->image.height > BT431_CURSOR_SIZE || |
109 | cursor->image.width > BT431_CURSOR_SIZE) { |
110 | bt431_erase_cursor(regs: par->bt431); |
111 | return -EINVAL; |
112 | } |
113 | |
114 | if (!cursor->enable) |
115 | bt431_erase_cursor(regs: par->bt431); |
116 | |
117 | if (cursor->set & FB_CUR_SETPOS) |
118 | bt431_position_cursor(regs: par->bt431, |
119 | x: cursor->image.dx, y: 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; |
123 | |
124 | bt455_write_cmap_entry(regs: par->bt455, cr: 8, grey: bg); |
125 | bt455_write_cmap_next(regs: par->bt455, grey: bg); |
126 | bt455_write_ovly_next(regs: par->bt455, grey: fg); |
127 | } |
128 | if (cursor->set & (FB_CUR_SETSIZE | FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) |
129 | bt431_set_cursor(regs: par->bt431, |
130 | data: cursor->image.data, mask: cursor->mask, rop: cursor->rop, |
131 | width: cursor->image.width, height: cursor->image.height); |
132 | |
133 | if (cursor->enable) |
134 | bt431_enable_cursor(regs: par->bt431); |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | /* 0 unblanks, any other blanks. */ |
140 | |
141 | static int aafb_blank(int blank, struct fb_info *info) |
142 | { |
143 | struct aafb_par *par = info->par; |
144 | u8 val = blank ? 0x00 : 0x0f; |
145 | |
146 | bt455_write_cmap_entry(regs: par->bt455, cr: 1, grey: val); |
147 | return 0; |
148 | } |
149 | |
150 | static const struct fb_ops aafb_ops = { |
151 | .owner = THIS_MODULE, |
152 | FB_DEFAULT_IOMEM_OPS, |
153 | .fb_blank = aafb_blank, |
154 | .fb_cursor = aafb_cursor, |
155 | }; |
156 | |
157 | static int pmagaafb_probe(struct device *dev) |
158 | { |
159 | struct tc_dev *tdev = to_tc_dev(dev); |
160 | resource_size_t start, len; |
161 | struct fb_info *info; |
162 | struct aafb_par *par; |
163 | int err; |
164 | |
165 | info = framebuffer_alloc(size: sizeof(struct aafb_par), dev); |
166 | if (!info) |
167 | return -ENOMEM; |
168 | |
169 | par = info->par; |
170 | dev_set_drvdata(dev, data: info); |
171 | |
172 | info->fbops = &aafb_ops; |
173 | info->fix = aafb_fix; |
174 | info->var = aafb_defined; |
175 | |
176 | /* Request the I/O MEM resource. */ |
177 | start = tdev->resource.start; |
178 | len = tdev->resource.end - start + 1; |
179 | if (!request_mem_region(start, len, dev_name(dev))) { |
180 | printk(KERN_ERR "%s: Cannot reserve FB region\n" , |
181 | dev_name(dev)); |
182 | err = -EBUSY; |
183 | goto err_alloc; |
184 | } |
185 | |
186 | /* MMIO mapping setup. */ |
187 | info->fix.mmio_start = start + PMAG_AA_BT455_OFFSET; |
188 | par->mmio = ioremap(offset: info->fix.mmio_start, size: info->fix.mmio_len); |
189 | if (!par->mmio) { |
190 | printk(KERN_ERR "%s: Cannot map MMIO\n" , dev_name(dev)); |
191 | err = -ENOMEM; |
192 | goto err_resource; |
193 | } |
194 | par->bt455 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT455_OFFSET; |
195 | par->bt431 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT431_OFFSET; |
196 | |
197 | /* Frame buffer mapping setup. */ |
198 | info->fix.smem_start = start + PMAG_AA_ONBOARD_FBMEM_OFFSET; |
199 | info->screen_base = ioremap(offset: info->fix.smem_start, |
200 | size: info->fix.smem_len); |
201 | if (!info->screen_base) { |
202 | printk(KERN_ERR "%s: Cannot map FB\n" , dev_name(dev)); |
203 | err = -ENOMEM; |
204 | goto err_mmio_map; |
205 | } |
206 | info->screen_size = info->fix.smem_len; |
207 | |
208 | /* Init colormap. */ |
209 | bt455_write_cmap_entry(regs: par->bt455, cr: 0, grey: 0x0); |
210 | bt455_write_cmap_next(regs: par->bt455, grey: 0xf); |
211 | |
212 | /* Init hardware cursor. */ |
213 | bt431_erase_cursor(regs: par->bt431); |
214 | bt431_init_cursor(regs: par->bt431); |
215 | |
216 | err = register_framebuffer(fb_info: info); |
217 | if (err < 0) { |
218 | printk(KERN_ERR "%s: Cannot register framebuffer\n" , |
219 | dev_name(dev)); |
220 | goto err_smem_map; |
221 | } |
222 | |
223 | get_device(dev); |
224 | |
225 | pr_info("fb%d: %s frame buffer device at %s\n" , |
226 | info->node, info->fix.id, dev_name(dev)); |
227 | |
228 | return 0; |
229 | |
230 | |
231 | err_smem_map: |
232 | iounmap(addr: info->screen_base); |
233 | |
234 | err_mmio_map: |
235 | iounmap(addr: par->mmio); |
236 | |
237 | err_resource: |
238 | release_mem_region(start, len); |
239 | |
240 | err_alloc: |
241 | framebuffer_release(info); |
242 | return err; |
243 | } |
244 | |
245 | static int pmagaafb_remove(struct device *dev) |
246 | { |
247 | struct tc_dev *tdev = to_tc_dev(dev); |
248 | struct fb_info *info = dev_get_drvdata(dev); |
249 | struct aafb_par *par = info->par; |
250 | resource_size_t start, len; |
251 | |
252 | put_device(dev); |
253 | unregister_framebuffer(fb_info: info); |
254 | iounmap(addr: info->screen_base); |
255 | iounmap(addr: par->mmio); |
256 | start = tdev->resource.start; |
257 | len = tdev->resource.end - start + 1; |
258 | release_mem_region(start, len); |
259 | framebuffer_release(info); |
260 | return 0; |
261 | } |
262 | |
263 | /* |
264 | * Initialise the framebuffer. |
265 | */ |
266 | static const struct tc_device_id pmagaafb_tc_table[] = { |
267 | { "DEC " , "PMAG-AA " }, |
268 | { } |
269 | }; |
270 | MODULE_DEVICE_TABLE(tc, pmagaafb_tc_table); |
271 | |
272 | static struct tc_driver pmagaafb_driver = { |
273 | .id_table = pmagaafb_tc_table, |
274 | .driver = { |
275 | .name = "pmagaafb" , |
276 | .bus = &tc_bus_type, |
277 | .probe = pmagaafb_probe, |
278 | .remove = pmagaafb_remove, |
279 | }, |
280 | }; |
281 | |
282 | static int __init pmagaafb_init(void) |
283 | { |
284 | #ifndef MODULE |
285 | if (fb_get_options(name: "pmagaafb" , NULL)) |
286 | return -ENXIO; |
287 | #endif |
288 | return tc_register_driver(tdrv: &pmagaafb_driver); |
289 | } |
290 | |
291 | static void __exit pmagaafb_exit(void) |
292 | { |
293 | tc_unregister_driver(tdrv: &pmagaafb_driver); |
294 | } |
295 | |
296 | module_init(pmagaafb_init); |
297 | module_exit(pmagaafb_exit); |
298 | |
299 | MODULE_AUTHOR(DRIVER_AUTHOR); |
300 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION); |
301 | MODULE_LICENSE("GPL" ); |
302 | |