blob: e2757ff1c23d2f90a7180f865b25066e4305131a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver
3 *
4 * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
5 * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
6 * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
7 *
8 * This file is subject to the terms and conditions of the GNU General
9 * Public License. See the file COPYING in the main directory of this
10 * archive for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/string.h>
17#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/delay.h>
19#include <linux/fb.h>
20#include <linux/ioport.h>
21#include <linux/init.h>
Antonino A. Daplas120ddb42005-11-08 21:39:16 -080022#include <linux/platform_device.h>
Jon Smirla8f340e2006-07-10 04:44:12 -070023#include <linux/screen_info.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#include <asm/io.h>
26#include <video/vga.h>
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#define VGA_FB_PHYS 0xA0000
29#define VGA_FB_PHYS_LEN 65536
30
31#define MODE_SKIP4 1
32#define MODE_8BPP 2
33#define MODE_CFB 4
34#define MODE_TEXT 8
35
36/* --------------------------------------------------------------------- */
37
38/*
39 * card parameters
40 */
41
Antonino A. Daplas120ddb42005-11-08 21:39:16 -080042struct vga16fb_par {
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 /* structure holding original VGA register settings when the
44 screen is blanked */
45 struct {
Antonino A. Daplas120ddb42005-11-08 21:39:16 -080046 unsigned char SeqCtrlIndex; /* Sequencer Index reg. */
47 unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */
48 unsigned char CrtMiscIO; /* Miscellaneous register */
49 unsigned char HorizontalTotal; /* CRT-Controller:00h */
50 unsigned char HorizDisplayEnd; /* CRT-Controller:01h */
51 unsigned char StartHorizRetrace;/* CRT-Controller:04h */
52 unsigned char EndHorizRetrace; /* CRT-Controller:05h */
53 unsigned char Overflow; /* CRT-Controller:07h */
54 unsigned char StartVertRetrace; /* CRT-Controller:10h */
55 unsigned char EndVertRetrace; /* CRT-Controller:11h */
56 unsigned char ModeControl; /* CRT-Controller:17h */
57 unsigned char ClockingMode; /* Seq-Controller:01h */
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 } vga_state;
59 struct vgastate state;
Jiri Slabyc4f28e52007-02-12 00:55:11 -080060 unsigned int ref_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 int palette_blanked, vesa_blanked, mode, isVGA;
62 u8 misc, pel_msk, vss, clkdiv;
63 u8 crtc[VGA_CRT_C];
Antonino A. Daplas120ddb42005-11-08 21:39:16 -080064};
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66/* --------------------------------------------------------------------- */
67
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -080068static struct fb_var_screeninfo vga16fb_defined = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 .xres = 640,
70 .yres = 480,
71 .xres_virtual = 640,
72 .yres_virtual = 480,
73 .bits_per_pixel = 4,
74 .activate = FB_ACTIVATE_TEST,
75 .height = -1,
76 .width = -1,
77 .pixclock = 39721,
78 .left_margin = 48,
79 .right_margin = 16,
80 .upper_margin = 33,
81 .lower_margin = 10,
82 .hsync_len = 96,
83 .vsync_len = 2,
84 .vmode = FB_VMODE_NONINTERLACED,
85};
86
87/* name should not depend on EGA/VGA */
Julia Lawallca9384c2016-09-11 17:17:20 +020088static const struct fb_fix_screeninfo vga16fb_fix = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 .id = "VGA16 VGA",
90 .smem_start = VGA_FB_PHYS,
91 .smem_len = VGA_FB_PHYS_LEN,
92 .type = FB_TYPE_VGA_PLANES,
93 .type_aux = FB_AUX_VGA_PLANES_VGA4,
94 .visual = FB_VISUAL_PSEUDOCOLOR,
95 .xpanstep = 8,
96 .ypanstep = 1,
Krzysztof Helt98219372008-07-23 21:31:23 -070097 .line_length = 640 / 8,
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 .accel = FB_ACCEL_NONE
99};
100
101/* The VGA's weird architecture often requires that we read a byte and
102 write a byte to the same location. It doesn't matter *what* byte
103 we write, however. This is because all the action goes on behind
104 the scenes in the VGA's 32-bit latch register, and reading and writing
105 video memory just invokes latch behavior.
106
107 To avoid race conditions (is this necessary?), reading and writing
108 the memory byte should be done with a single instruction. One
109 suitable instruction is the x86 bitwise OR. The following
110 read-modify-write routine should optimize to one such bitwise
111 OR. */
112static inline void rmw(volatile char __iomem *p)
113{
114 readb(p);
115 writeb(1, p);
116}
117
118/* Set the Graphics Mode Register, and return its previous value.
119 Bits 0-1 are write mode, bit 3 is read mode. */
120static inline int setmode(int mode)
121{
122 int oldmode;
123
Krzysztof Helt98219372008-07-23 21:31:23 -0700124 oldmode = vga_io_rgfx(VGA_GFX_MODE);
125 vga_io_w(VGA_GFX_D, mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 return oldmode;
127}
128
129/* Select the Bit Mask Register and return its value. */
130static inline int selectmask(void)
131{
Krzysztof Helt98219372008-07-23 21:31:23 -0700132 return vga_io_rgfx(VGA_GFX_BIT_MASK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133}
134
135/* Set the value of the Bit Mask Register. It must already have been
136 selected with selectmask(). */
137static inline void setmask(int mask)
138{
Krzysztof Helt98219372008-07-23 21:31:23 -0700139 vga_io_w(VGA_GFX_D, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
142/* Set the Data Rotate Register and return its old value.
143 Bits 0-2 are rotate count, bits 3-4 are logical operation
144 (0=NOP, 1=AND, 2=OR, 3=XOR). */
145static inline int setop(int op)
146{
147 int oldop;
148
Krzysztof Helt98219372008-07-23 21:31:23 -0700149 oldop = vga_io_rgfx(VGA_GFX_DATA_ROTATE);
150 vga_io_w(VGA_GFX_D, op);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 return oldop;
152}
153
154/* Set the Enable Set/Reset Register and return its old value.
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300155 The code here always uses value 0xf for this register. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156static inline int setsr(int sr)
157{
158 int oldsr;
159
Krzysztof Helt98219372008-07-23 21:31:23 -0700160 oldsr = vga_io_rgfx(VGA_GFX_SR_ENABLE);
161 vga_io_w(VGA_GFX_D, sr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 return oldsr;
163}
164
165/* Set the Set/Reset Register and return its old value. */
166static inline int setcolor(int color)
167{
168 int oldcolor;
169
Krzysztof Helt98219372008-07-23 21:31:23 -0700170 oldcolor = vga_io_rgfx(VGA_GFX_SR_VALUE);
171 vga_io_w(VGA_GFX_D, color);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 return oldcolor;
173}
174
175/* Return the value in the Graphics Address Register. */
176static inline int getindex(void)
177{
Krzysztof Helt98219372008-07-23 21:31:23 -0700178 return vga_io_r(VGA_GFX_I);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179}
180
181/* Set the value in the Graphics Address Register. */
182static inline void setindex(int index)
183{
Krzysztof Helt98219372008-07-23 21:31:23 -0700184 vga_io_w(VGA_GFX_I, index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185}
186
187static void vga16fb_pan_var(struct fb_info *info,
188 struct fb_var_screeninfo *var)
189{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800190 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 u32 xoffset, pos;
192
193 xoffset = var->xoffset;
194 if (info->var.bits_per_pixel == 8) {
195 pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 2;
196 } else if (par->mode & MODE_TEXT) {
197 int fh = 16; // FIXME !!! font height. Fugde for now.
198 pos = (info->var.xres_virtual * (var->yoffset / fh) + xoffset) >> 3;
199 } else {
200 if (info->var.nonstd)
201 xoffset--;
202 pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 3;
203 }
204 vga_io_wcrt(VGA_CRTC_START_HI, pos >> 8);
205 vga_io_wcrt(VGA_CRTC_START_LO, pos & 0xFF);
206 /* if we support CFB4, then we must! support xoffset with pixel
207 * granularity if someone supports xoffset in bit resolution */
208 vga_io_r(VGA_IS1_RC); /* reset flip-flop */
209 vga_io_w(VGA_ATT_IW, VGA_ATC_PEL);
Laurent Pinchartc272d642011-05-25 11:34:52 +0200210 if (info->var.bits_per_pixel == 8)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 vga_io_w(VGA_ATT_IW, (xoffset & 3) << 1);
212 else
213 vga_io_w(VGA_ATT_IW, xoffset & 7);
214 vga_io_r(VGA_IS1_RC);
215 vga_io_w(VGA_ATT_IW, 0x20);
216}
217
218static void vga16fb_update_fix(struct fb_info *info)
219{
220 if (info->var.bits_per_pixel == 4) {
221 if (info->var.nonstd) {
222 info->fix.type = FB_TYPE_PACKED_PIXELS;
223 info->fix.line_length = info->var.xres_virtual / 2;
224 } else {
225 info->fix.type = FB_TYPE_VGA_PLANES;
226 info->fix.type_aux = FB_AUX_VGA_PLANES_VGA4;
227 info->fix.line_length = info->var.xres_virtual / 8;
228 }
229 } else if (info->var.bits_per_pixel == 0) {
230 info->fix.type = FB_TYPE_TEXT;
231 info->fix.type_aux = FB_AUX_TEXT_CGA;
232 info->fix.line_length = info->var.xres_virtual / 4;
233 } else { /* 8bpp */
234 if (info->var.nonstd) {
235 info->fix.type = FB_TYPE_VGA_PLANES;
236 info->fix.type_aux = FB_AUX_VGA_PLANES_CFB8;
237 info->fix.line_length = info->var.xres_virtual / 4;
238 } else {
239 info->fix.type = FB_TYPE_PACKED_PIXELS;
240 info->fix.line_length = info->var.xres_virtual;
241 }
242 }
243}
244
245static void vga16fb_clock_chip(struct vga16fb_par *par,
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100246 unsigned int *pixclock,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 const struct fb_info *info,
248 int mul, int div)
249{
Helge Dellerec1a7b32006-12-08 02:40:31 -0800250 static const struct {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 u32 pixclock;
252 u8 misc;
253 u8 seq_clock_mode;
254 } *ptr, *best, vgaclocks[] = {
255 { 79442 /* 12.587 */, 0x00, 0x08},
256 { 70616 /* 14.161 */, 0x04, 0x08},
257 { 39721 /* 25.175 */, 0x00, 0x00},
258 { 35308 /* 28.322 */, 0x04, 0x00},
259 { 0 /* bad */, 0x00, 0x00}};
260 int err;
261
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100262 *pixclock = (*pixclock * mul) / div;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 best = vgaclocks;
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100264 err = *pixclock - best->pixclock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 if (err < 0) err = -err;
266 for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) {
267 int tmp;
268
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100269 tmp = *pixclock - ptr->pixclock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 if (tmp < 0) tmp = -tmp;
271 if (tmp < err) {
272 err = tmp;
273 best = ptr;
274 }
275 }
276 par->misc |= best->misc;
277 par->clkdiv = best->seq_clock_mode;
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100278 *pixclock = (best->pixclock * div) / mul;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279}
280
281#define FAIL(X) return -EINVAL
282
283static int vga16fb_open(struct fb_info *info, int user)
284{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800285 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Jiri Slabyc4f28e52007-02-12 00:55:11 -0800287 if (!par->ref_count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 memset(&par->state, 0, sizeof(struct vgastate));
289 par->state.flags = VGA_SAVE_FONTS | VGA_SAVE_MODE |
290 VGA_SAVE_CMAP;
291 save_vga(&par->state);
292 }
Jiri Slabyc4f28e52007-02-12 00:55:11 -0800293 par->ref_count++;
Jiri Slabyc4f28e52007-02-12 00:55:11 -0800294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 return 0;
296}
297
298static int vga16fb_release(struct fb_info *info, int user)
299{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800300 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Krzysztof Helt9c8db4a2008-10-15 22:03:33 -0700302 if (!par->ref_count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 return -EINVAL;
Krzysztof Helt9c8db4a2008-10-15 22:03:33 -0700304
Jiri Slabyc4f28e52007-02-12 00:55:11 -0800305 if (par->ref_count == 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 restore_vga(&par->state);
Jiri Slabyc4f28e52007-02-12 00:55:11 -0800307 par->ref_count--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 return 0;
310}
311
312static int vga16fb_check_var(struct fb_var_screeninfo *var,
313 struct fb_info *info)
314{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800315 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 u32 xres, right, hslen, left, xtotal;
317 u32 yres, lower, vslen, upper, ytotal;
318 u32 vxres, xoffset, vyres, yoffset;
319 u32 pos;
320 u8 r7, rMode;
321 int shift;
322 int mode;
323 u32 maxmem;
324
325 par->pel_msk = 0xFF;
326
327 if (var->bits_per_pixel == 4) {
328 if (var->nonstd) {
329 if (!par->isVGA)
330 return -EINVAL;
331 shift = 3;
332 mode = MODE_SKIP4 | MODE_CFB;
333 maxmem = 16384;
334 par->pel_msk = 0x0F;
335 } else {
336 shift = 3;
337 mode = 0;
338 maxmem = 65536;
339 }
340 } else if (var->bits_per_pixel == 8) {
341 if (!par->isVGA)
342 return -EINVAL; /* no support on EGA */
343 shift = 2;
344 if (var->nonstd) {
345 mode = MODE_8BPP | MODE_CFB;
346 maxmem = 65536;
347 } else {
348 mode = MODE_SKIP4 | MODE_8BPP | MODE_CFB;
349 maxmem = 16384;
350 }
351 } else
352 return -EINVAL;
353
354 xres = (var->xres + 7) & ~7;
355 vxres = (var->xres_virtual + 0xF) & ~0xF;
356 xoffset = (var->xoffset + 7) & ~7;
357 left = (var->left_margin + 7) & ~7;
358 right = (var->right_margin + 7) & ~7;
359 hslen = (var->hsync_len + 7) & ~7;
360
361 if (vxres < xres)
362 vxres = xres;
363 if (xres + xoffset > vxres)
364 xoffset = vxres - xres;
365
366 var->xres = xres;
367 var->right_margin = right;
368 var->hsync_len = hslen;
369 var->left_margin = left;
370 var->xres_virtual = vxres;
371 var->xoffset = xoffset;
372
373 xres >>= shift;
374 right >>= shift;
375 hslen >>= shift;
376 left >>= shift;
377 vxres >>= shift;
378 xtotal = xres + right + hslen + left;
379 if (xtotal >= 256)
380 FAIL("xtotal too big");
381 if (hslen > 32)
382 FAIL("hslen too big");
383 if (right + hslen + left > 64)
384 FAIL("hblank too big");
385 par->crtc[VGA_CRTC_H_TOTAL] = xtotal - 5;
386 par->crtc[VGA_CRTC_H_BLANK_START] = xres - 1;
387 par->crtc[VGA_CRTC_H_DISP] = xres - 1;
388 pos = xres + right;
389 par->crtc[VGA_CRTC_H_SYNC_START] = pos;
390 pos += hslen;
391 par->crtc[VGA_CRTC_H_SYNC_END] = pos & 0x1F;
392 pos += left - 2; /* blank_end + 2 <= total + 5 */
393 par->crtc[VGA_CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
394 if (pos & 0x20)
395 par->crtc[VGA_CRTC_H_SYNC_END] |= 0x80;
396
397 yres = var->yres;
398 lower = var->lower_margin;
399 vslen = var->vsync_len;
400 upper = var->upper_margin;
401 vyres = var->yres_virtual;
402 yoffset = var->yoffset;
403
404 if (yres > vyres)
405 vyres = yres;
406 if (vxres * vyres > maxmem) {
407 vyres = maxmem / vxres;
408 if (vyres < yres)
409 return -ENOMEM;
410 }
411 if (yoffset + yres > vyres)
412 yoffset = vyres - yres;
413 var->yres = yres;
414 var->lower_margin = lower;
415 var->vsync_len = vslen;
416 var->upper_margin = upper;
417 var->yres_virtual = vyres;
418 var->yoffset = yoffset;
419
420 if (var->vmode & FB_VMODE_DOUBLE) {
421 yres <<= 1;
422 lower <<= 1;
423 vslen <<= 1;
424 upper <<= 1;
425 }
426 ytotal = yres + lower + vslen + upper;
427 if (ytotal > 1024) {
428 ytotal >>= 1;
429 yres >>= 1;
430 lower >>= 1;
431 vslen >>= 1;
432 upper >>= 1;
433 rMode = 0x04;
434 } else
435 rMode = 0x00;
436 if (ytotal > 1024)
437 FAIL("ytotal too big");
438 if (vslen > 16)
439 FAIL("vslen too big");
440 par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2;
441 r7 = 0x10; /* disable linecompare */
442 if (ytotal & 0x100) r7 |= 0x01;
443 if (ytotal & 0x200) r7 |= 0x20;
444 par->crtc[VGA_CRTC_PRESET_ROW] = 0;
445 par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */
446 if (var->vmode & FB_VMODE_DOUBLE)
447 par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80;
448 par->crtc[VGA_CRTC_CURSOR_START] = 0x20;
449 par->crtc[VGA_CRTC_CURSOR_END] = 0x00;
450 if ((mode & (MODE_CFB | MODE_8BPP)) == MODE_CFB)
451 xoffset--;
452 pos = yoffset * vxres + (xoffset >> shift);
453 par->crtc[VGA_CRTC_START_HI] = pos >> 8;
454 par->crtc[VGA_CRTC_START_LO] = pos & 0xFF;
455 par->crtc[VGA_CRTC_CURSOR_HI] = 0x00;
456 par->crtc[VGA_CRTC_CURSOR_LO] = 0x00;
457 pos = yres - 1;
458 par->crtc[VGA_CRTC_V_DISP_END] = pos & 0xFF;
459 par->crtc[VGA_CRTC_V_BLANK_START] = pos & 0xFF;
460 if (pos & 0x100)
461 r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */
462 if (pos & 0x200) {
463 r7 |= 0x40; /* 0x40 -> DISP_END */
464 par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
465 }
466 pos += lower;
467 par->crtc[VGA_CRTC_V_SYNC_START] = pos & 0xFF;
468 if (pos & 0x100)
469 r7 |= 0x04;
470 if (pos & 0x200)
471 r7 |= 0x80;
472 pos += vslen;
473 par->crtc[VGA_CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled IRQ */
474 pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
475 par->crtc[VGA_CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
476 but some SVGA chips requires all 8 bits to set */
477 if (vxres >= 512)
478 FAIL("vxres too long");
479 par->crtc[VGA_CRTC_OFFSET] = vxres >> 1;
480 if (mode & MODE_SKIP4)
481 par->crtc[VGA_CRTC_UNDERLINE] = 0x5F; /* 256, cfb8 */
482 else
483 par->crtc[VGA_CRTC_UNDERLINE] = 0x1F; /* 16, vgap */
484 par->crtc[VGA_CRTC_MODE] = rMode | ((mode & MODE_TEXT) ? 0xA3 : 0xE3);
485 par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF;
486 par->crtc[VGA_CRTC_OVERFLOW] = r7;
487
488 par->vss = 0x00; /* 3DA */
489
490 par->misc = 0xE3; /* enable CPU, ports 0x3Dx, positive sync */
491 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
492 par->misc &= ~0x40;
493 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
494 par->misc &= ~0x80;
495
496 par->mode = mode;
497
498 if (mode & MODE_8BPP)
499 /* pixel clock == vga clock / 2 */
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100500 vga16fb_clock_chip(par, &var->pixclock, info, 1, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 else
502 /* pixel clock == vga clock */
Colin Ian Kingc72fab82020-07-23 18:02:27 +0100503 vga16fb_clock_chip(par, &var->pixclock, info, 1, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 var->red.offset = var->green.offset = var->blue.offset =
506 var->transp.offset = 0;
507 var->red.length = var->green.length = var->blue.length =
508 (par->isVGA) ? 6 : 2;
509 var->transp.length = 0;
510 var->activate = FB_ACTIVATE_NOW;
511 var->height = -1;
512 var->width = -1;
513 var->accel_flags = 0;
514 return 0;
515}
516#undef FAIL
517
518static int vga16fb_set_par(struct fb_info *info)
519{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800520 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 u8 gdc[VGA_GFX_C];
522 u8 seq[VGA_SEQ_C];
523 u8 atc[VGA_ATT_C];
524 int fh, i;
525
526 seq[VGA_SEQ_CLOCK_MODE] = 0x01 | par->clkdiv;
527 if (par->mode & MODE_TEXT)
528 seq[VGA_SEQ_PLANE_WRITE] = 0x03;
529 else
530 seq[VGA_SEQ_PLANE_WRITE] = 0x0F;
531 seq[VGA_SEQ_CHARACTER_MAP] = 0x00;
532 if (par->mode & MODE_TEXT)
533 seq[VGA_SEQ_MEMORY_MODE] = 0x03;
534 else if (par->mode & MODE_SKIP4)
535 seq[VGA_SEQ_MEMORY_MODE] = 0x0E;
536 else
537 seq[VGA_SEQ_MEMORY_MODE] = 0x06;
538
539 gdc[VGA_GFX_SR_VALUE] = 0x00;
540 gdc[VGA_GFX_SR_ENABLE] = 0x00;
541 gdc[VGA_GFX_COMPARE_VALUE] = 0x00;
542 gdc[VGA_GFX_DATA_ROTATE] = 0x00;
543 gdc[VGA_GFX_PLANE_READ] = 0;
544 if (par->mode & MODE_TEXT) {
545 gdc[VGA_GFX_MODE] = 0x10;
546 gdc[VGA_GFX_MISC] = 0x06;
547 } else {
548 if (par->mode & MODE_CFB)
549 gdc[VGA_GFX_MODE] = 0x40;
550 else
551 gdc[VGA_GFX_MODE] = 0x00;
552 gdc[VGA_GFX_MISC] = 0x05;
553 }
554 gdc[VGA_GFX_COMPARE_MASK] = 0x0F;
555 gdc[VGA_GFX_BIT_MASK] = 0xFF;
556
557 for (i = 0x00; i < 0x10; i++)
558 atc[i] = i;
559 if (par->mode & MODE_TEXT)
560 atc[VGA_ATC_MODE] = 0x04;
561 else if (par->mode & MODE_8BPP)
562 atc[VGA_ATC_MODE] = 0x41;
563 else
564 atc[VGA_ATC_MODE] = 0x81;
565 atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */
566 atc[VGA_ATC_PLANE_ENABLE] = 0x0F;
567 if (par->mode & MODE_8BPP)
568 atc[VGA_ATC_PEL] = (info->var.xoffset & 3) << 1;
569 else
570 atc[VGA_ATC_PEL] = info->var.xoffset & 7;
571 atc[VGA_ATC_COLOR_PAGE] = 0x00;
572
573 if (par->mode & MODE_TEXT) {
574 fh = 16; // FIXME !!! Fudge font height.
575 par->crtc[VGA_CRTC_MAX_SCAN] = (par->crtc[VGA_CRTC_MAX_SCAN]
576 & ~0x1F) | (fh - 1);
577 }
578
579 vga_io_w(VGA_MIS_W, vga_io_r(VGA_MIS_R) | 0x01);
580
581 /* Enable graphics register modification */
582 if (!par->isVGA) {
583 vga_io_w(EGA_GFX_E0, 0x00);
584 vga_io_w(EGA_GFX_E1, 0x01);
585 }
586
587 /* update misc output register */
588 vga_io_w(VGA_MIS_W, par->misc);
589
590 /* synchronous reset on */
591 vga_io_wseq(0x00, 0x01);
592
593 if (par->isVGA)
594 vga_io_w(VGA_PEL_MSK, par->pel_msk);
595
596 /* write sequencer registers */
597 vga_io_wseq(VGA_SEQ_CLOCK_MODE, seq[VGA_SEQ_CLOCK_MODE] | 0x20);
598 for (i = 2; i < VGA_SEQ_C; i++) {
599 vga_io_wseq(i, seq[i]);
600 }
601
602 /* synchronous reset off */
603 vga_io_wseq(0x00, 0x03);
604
605 /* deprotect CRT registers 0-7 */
606 vga_io_wcrt(VGA_CRTC_V_SYNC_END, par->crtc[VGA_CRTC_V_SYNC_END]);
607
608 /* write CRT registers */
609 for (i = 0; i < VGA_CRTC_REGS; i++) {
610 vga_io_wcrt(i, par->crtc[i]);
611 }
612
613 /* write graphics controller registers */
614 for (i = 0; i < VGA_GFX_C; i++) {
615 vga_io_wgfx(i, gdc[i]);
616 }
617
618 /* write attribute controller registers */
619 for (i = 0; i < VGA_ATT_C; i++) {
620 vga_io_r(VGA_IS1_RC); /* reset flip-flop */
621 vga_io_wattr(i, atc[i]);
622 }
623
624 /* Wait for screen to stabilize. */
625 mdelay(50);
626
627 vga_io_wseq(VGA_SEQ_CLOCK_MODE, seq[VGA_SEQ_CLOCK_MODE]);
628
629 vga_io_r(VGA_IS1_RC);
630 vga_io_w(VGA_ATT_IW, 0x20);
631
632 vga16fb_update_fix(info);
633 return 0;
634}
635
636static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
637{
Helge Dellerec1a7b32006-12-08 02:40:31 -0800638 static const unsigned char map[] = { 000, 001, 010, 011 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 int val;
640
641 if (regno >= 16)
642 return;
643 val = map[red>>14] | ((map[green>>14]) << 1) | ((map[blue>>14]) << 2);
644 vga_io_r(VGA_IS1_RC); /* ! 0x3BA */
645 vga_io_wattr(regno, val);
646 vga_io_r(VGA_IS1_RC); /* some clones need it */
647 vga_io_w(VGA_ATT_IW, 0x20); /* unblank screen */
648}
649
650static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
651{
Krzysztof Helt98219372008-07-23 21:31:23 -0700652 outb(regno, VGA_PEL_IW);
653 outb(red >> 10, VGA_PEL_D);
654 outb(green >> 10, VGA_PEL_D);
655 outb(blue >> 10, VGA_PEL_D);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
658static int vga16fb_setcolreg(unsigned regno, unsigned red, unsigned green,
659 unsigned blue, unsigned transp,
660 struct fb_info *info)
661{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800662 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 int gray;
664
665 /*
666 * Set a single color register. The values supplied are
667 * already rounded down to the hardware's capabilities
668 * (according to the entries in the `var' structure). Return
669 * != 0 for invalid regno.
670 */
671
672 if (regno >= 256)
673 return 1;
674
675 gray = info->var.grayscale;
676
677 if (gray) {
678 /* gray = 0.30*R + 0.59*G + 0.11*B */
679 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
680 }
681 if (par->isVGA)
682 vga16_setpalette(regno,red,green,blue);
683 else
684 ega16_setpalette(regno,red,green,blue);
685 return 0;
686}
687
688static int vga16fb_pan_display(struct fb_var_screeninfo *var,
689 struct fb_info *info)
690{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 vga16fb_pan_var(info, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 return 0;
693}
694
695/* The following VESA blanking code is taken from vgacon.c. The VGA
696 blanking code was originally by Huang shi chao, and modified by
697 Christoph Rimek (chrimek@toppoint.de) and todd j. derr
698 (tjd@barefoot.org) for Linux. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
700static void vga_vesa_blank(struct vga16fb_par *par, int mode)
701{
Krzysztof Helt98219372008-07-23 21:31:23 -0700702 unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
703 unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 /* save original values of VGA controller registers */
706 if(!par->vesa_blanked) {
Krzysztof Helt98219372008-07-23 21:31:23 -0700707 par->vga_state.CrtMiscIO = vga_io_r(VGA_MIS_R);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 //sti();
709
710 par->vga_state.HorizontalTotal = vga_io_rcrt(0x00); /* HorizontalTotal */
711 par->vga_state.HorizDisplayEnd = vga_io_rcrt(0x01); /* HorizDisplayEnd */
712 par->vga_state.StartHorizRetrace = vga_io_rcrt(0x04); /* StartHorizRetrace */
713 par->vga_state.EndHorizRetrace = vga_io_rcrt(0x05); /* EndHorizRetrace */
714 par->vga_state.Overflow = vga_io_rcrt(0x07); /* Overflow */
715 par->vga_state.StartVertRetrace = vga_io_rcrt(0x10); /* StartVertRetrace */
716 par->vga_state.EndVertRetrace = vga_io_rcrt(0x11); /* EndVertRetrace */
717 par->vga_state.ModeControl = vga_io_rcrt(0x17); /* ModeControl */
718 par->vga_state.ClockingMode = vga_io_rseq(0x01); /* ClockingMode */
719 }
720
721 /* assure that video is enabled */
722 /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 vga_io_wseq(0x01, par->vga_state.ClockingMode | 0x20);
724
725 /* test for vertical retrace in process.... */
726 if ((par->vga_state.CrtMiscIO & 0x80) == 0x80)
Krzysztof Helt98219372008-07-23 21:31:23 -0700727 vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO & 0xef);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 /*
730 * Set <End of vertical retrace> to minimum (0) and
731 * <Start of vertical Retrace> to maximum (incl. overflow)
732 * Result: turn off vertical sync (VSync) pulse.
733 */
734 if (mode & FB_BLANK_VSYNC_SUSPEND) {
Krzysztof Helt98219372008-07-23 21:31:23 -0700735 vga_io_wcrt(VGA_CRTC_V_SYNC_START, 0xff);
736 vga_io_wcrt(VGA_CRTC_V_SYNC_END, 0x40);
737 /* bits 9,10 of vert. retrace */
738 vga_io_wcrt(VGA_CRTC_OVERFLOW, par->vga_state.Overflow | 0x84);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 }
740
741 if (mode & FB_BLANK_HSYNC_SUSPEND) {
742 /*
743 * Set <End of horizontal retrace> to minimum (0) and
744 * <Start of horizontal Retrace> to maximum
745 * Result: turn off horizontal sync (HSync) pulse.
746 */
Krzysztof Helt98219372008-07-23 21:31:23 -0700747 vga_io_wcrt(VGA_CRTC_H_SYNC_START, 0xff);
748 vga_io_wcrt(VGA_CRTC_H_SYNC_END, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
750
751 /* restore both index registers */
Krzysztof Helt98219372008-07-23 21:31:23 -0700752 outb_p(SeqCtrlIndex, VGA_SEQ_I);
753 outb_p(CrtCtrlIndex, VGA_CRT_IC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
755
756static void vga_vesa_unblank(struct vga16fb_par *par)
757{
Krzysztof Helt98219372008-07-23 21:31:23 -0700758 unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
759 unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 /* restore original values of VGA controller registers */
Krzysztof Helt98219372008-07-23 21:31:23 -0700762 vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
764 /* HorizontalTotal */
765 vga_io_wcrt(0x00, par->vga_state.HorizontalTotal);
766 /* HorizDisplayEnd */
767 vga_io_wcrt(0x01, par->vga_state.HorizDisplayEnd);
768 /* StartHorizRetrace */
769 vga_io_wcrt(0x04, par->vga_state.StartHorizRetrace);
770 /* EndHorizRetrace */
771 vga_io_wcrt(0x05, par->vga_state.EndHorizRetrace);
772 /* Overflow */
773 vga_io_wcrt(0x07, par->vga_state.Overflow);
774 /* StartVertRetrace */
775 vga_io_wcrt(0x10, par->vga_state.StartVertRetrace);
776 /* EndVertRetrace */
777 vga_io_wcrt(0x11, par->vga_state.EndVertRetrace);
778 /* ModeControl */
779 vga_io_wcrt(0x17, par->vga_state.ModeControl);
780 /* ClockingMode */
781 vga_io_wseq(0x01, par->vga_state.ClockingMode);
782
783 /* restore index/control registers */
Krzysztof Helt98219372008-07-23 21:31:23 -0700784 vga_io_w(VGA_SEQ_I, SeqCtrlIndex);
785 vga_io_w(VGA_CRT_IC, CrtCtrlIndex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786}
787
788static void vga_pal_blank(void)
789{
790 int i;
791
792 for (i=0; i<16; i++) {
Krzysztof Helt98219372008-07-23 21:31:23 -0700793 outb_p(i, VGA_PEL_IW);
794 outb_p(0, VGA_PEL_D);
795 outb_p(0, VGA_PEL_D);
796 outb_p(0, VGA_PEL_D);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
798}
799
800/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
801static int vga16fb_blank(int blank, struct fb_info *info)
802{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -0800803 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805 switch (blank) {
806 case FB_BLANK_UNBLANK: /* Unblank */
807 if (par->vesa_blanked) {
808 vga_vesa_unblank(par);
809 par->vesa_blanked = 0;
810 }
811 if (par->palette_blanked) {
812 par->palette_blanked = 0;
813 }
814 break;
815 case FB_BLANK_NORMAL: /* blank */
816 vga_pal_blank();
817 par->palette_blanked = 1;
818 break;
819 default: /* VESA blanking */
820 vga_vesa_blank(par, blank);
821 par->vesa_blanked = 1;
822 break;
823 }
824 return 0;
825}
826
827static void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
828{
829 u32 dx = rect->dx, width = rect->width;
830 char oldindex = getindex();
831 char oldmode = setmode(0x40);
832 char oldmask = selectmask();
833 int line_ofs, height;
834 char oldop, oldsr;
835 char __iomem *where;
836
837 dx /= 4;
838 where = info->screen_base + dx + rect->dy * info->fix.line_length;
839
840 if (rect->rop == ROP_COPY) {
841 oldop = setop(0);
842 oldsr = setsr(0);
843
844 width /= 4;
845 line_ofs = info->fix.line_length - width;
846 setmask(0xff);
847
848 height = rect->height;
849
850 while (height--) {
851 int x;
852
853 /* we can do memset... */
854 for (x = width; x > 0; --x) {
855 writeb(rect->color, where);
856 where++;
857 }
858 where += line_ofs;
859 }
860 } else {
861 char oldcolor = setcolor(0xf);
862 int y;
863
864 oldop = setop(0x18);
865 oldsr = setsr(0xf);
866 setmask(0x0F);
867 for (y = 0; y < rect->height; y++) {
868 rmw(where);
869 rmw(where+1);
870 where += info->fix.line_length;
871 }
872 setcolor(oldcolor);
873 }
874 setmask(oldmask);
875 setsr(oldsr);
876 setop(oldop);
877 setmode(oldmode);
878 setindex(oldindex);
879}
880
881static void vga16fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
882{
883 int x, x2, y2, vxres, vyres, width, height, line_ofs;
884 char __iomem *dst;
885
886 vxres = info->var.xres_virtual;
887 vyres = info->var.yres_virtual;
888
889 if (!rect->width || !rect->height || rect->dx > vxres || rect->dy > vyres)
890 return;
891
892 /* We could use hardware clipping but on many cards you get around
893 * hardware clipping by writing to framebuffer directly. */
894
895 x2 = rect->dx + rect->width;
896 y2 = rect->dy + rect->height;
897 x2 = x2 < vxres ? x2 : vxres;
898 y2 = y2 < vyres ? y2 : vyres;
899 width = x2 - rect->dx;
900
901 switch (info->fix.type) {
902 case FB_TYPE_VGA_PLANES:
903 if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
904
905 height = y2 - rect->dy;
906 width = rect->width/8;
907
908 line_ofs = info->fix.line_length - width;
909 dst = info->screen_base + (rect->dx/8) + rect->dy * info->fix.line_length;
910
911 switch (rect->rop) {
912 case ROP_COPY:
913 setmode(0);
914 setop(0);
915 setsr(0xf);
916 setcolor(rect->color);
917 selectmask();
918
919 setmask(0xff);
920
921 while (height--) {
922 for (x = 0; x < width; x++) {
923 writeb(0, dst);
924 dst++;
925 }
926 dst += line_ofs;
927 }
928 break;
929 case ROP_XOR:
930 setmode(0);
931 setop(0x18);
932 setsr(0xf);
933 setcolor(0xf);
934 selectmask();
935
936 setmask(0xff);
937 while (height--) {
938 for (x = 0; x < width; x++) {
939 rmw(dst);
940 dst++;
941 }
942 dst += line_ofs;
943 }
944 break;
945 }
946 } else
947 vga_8planes_fillrect(info, rect);
948 break;
949 case FB_TYPE_PACKED_PIXELS:
950 default:
951 cfb_fillrect(info, rect);
952 break;
953 }
954}
955
956static void vga_8planes_copyarea(struct fb_info *info, const struct fb_copyarea *area)
957{
958 char oldindex = getindex();
959 char oldmode = setmode(0x41);
960 char oldop = setop(0);
961 char oldsr = setsr(0xf);
962 int height, line_ofs, x;
963 u32 sx, dx, width;
964 char __iomem *dest;
965 char __iomem *src;
966
967 height = area->height;
968
969 sx = area->sx / 4;
970 dx = area->dx / 4;
971 width = area->width / 4;
972
973 if (area->dy < area->sy || (area->dy == area->sy && dx < sx)) {
974 line_ofs = info->fix.line_length - width;
975 dest = info->screen_base + dx + area->dy * info->fix.line_length;
976 src = info->screen_base + sx + area->sy * info->fix.line_length;
977 while (height--) {
978 for (x = 0; x < width; x++) {
979 readb(src);
980 writeb(0, dest);
981 src++;
982 dest++;
983 }
984 src += line_ofs;
985 dest += line_ofs;
986 }
987 } else {
988 line_ofs = info->fix.line_length - width;
989 dest = info->screen_base + dx + width +
990 (area->dy + height - 1) * info->fix.line_length;
991 src = info->screen_base + sx + width +
992 (area->sy + height - 1) * info->fix.line_length;
993 while (height--) {
994 for (x = 0; x < width; x++) {
995 --src;
996 --dest;
997 readb(src);
998 writeb(0, dest);
999 }
1000 src -= line_ofs;
1001 dest -= line_ofs;
1002 }
1003 }
1004
1005 setsr(oldsr);
1006 setop(oldop);
1007 setmode(oldmode);
1008 setindex(oldindex);
1009}
1010
1011static void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
1012{
1013 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
1014 int x, x2, y2, old_dx, old_dy, vxres, vyres;
1015 int height, width, line_ofs;
1016 char __iomem *dst = NULL;
1017 char __iomem *src = NULL;
1018
1019 vxres = info->var.xres_virtual;
1020 vyres = info->var.yres_virtual;
1021
1022 if (area->dx > vxres || area->sx > vxres || area->dy > vyres ||
1023 area->sy > vyres)
1024 return;
1025
1026 /* clip the destination */
1027 old_dx = area->dx;
1028 old_dy = area->dy;
1029
1030 /*
1031 * We could use hardware clipping but on many cards you get around
1032 * hardware clipping by writing to framebuffer directly.
1033 */
1034 x2 = area->dx + area->width;
1035 y2 = area->dy + area->height;
1036 dx = area->dx > 0 ? area->dx : 0;
1037 dy = area->dy > 0 ? area->dy : 0;
1038 x2 = x2 < vxres ? x2 : vxres;
1039 y2 = y2 < vyres ? y2 : vyres;
1040 width = x2 - dx;
1041 height = y2 - dy;
1042
Roel Kluin77a6e7a2008-07-23 21:31:19 -07001043 if (sx + dx < old_dx || sy + dy < old_dy)
1044 return;
1045
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 /* update sx1,sy1 */
1047 sx += (dx - old_dx);
1048 sy += (dy - old_dy);
1049
1050 /* the source must be completely inside the virtual screen */
Roel Kluin77a6e7a2008-07-23 21:31:19 -07001051 if (sx + width > vxres || sy + height > vyres)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 return;
1053
1054 switch (info->fix.type) {
1055 case FB_TYPE_VGA_PLANES:
1056 if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
1057 width = width/8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 line_ofs = info->fix.line_length - width;
1059
1060 setmode(1);
1061 setop(0);
1062 setsr(0xf);
1063
1064 if (dy < sy || (dy == sy && dx < sx)) {
1065 dst = info->screen_base + (dx/8) + dy * info->fix.line_length;
1066 src = info->screen_base + (sx/8) + sy * info->fix.line_length;
1067 while (height--) {
1068 for (x = 0; x < width; x++) {
1069 readb(src);
1070 writeb(0, dst);
1071 dst++;
1072 src++;
1073 }
1074 src += line_ofs;
1075 dst += line_ofs;
1076 }
1077 } else {
1078 dst = info->screen_base + (dx/8) + width +
1079 (dy + height - 1) * info->fix.line_length;
1080 src = info->screen_base + (sx/8) + width +
1081 (sy + height - 1) * info->fix.line_length;
1082 while (height--) {
1083 for (x = 0; x < width; x++) {
1084 dst--;
1085 src--;
1086 readb(src);
1087 writeb(0, dst);
1088 }
1089 src -= line_ofs;
1090 dst -= line_ofs;
1091 }
1092 }
1093 } else
1094 vga_8planes_copyarea(info, area);
1095 break;
1096 case FB_TYPE_PACKED_PIXELS:
1097 default:
1098 cfb_copyarea(info, area);
1099 break;
1100 }
1101}
1102
Helge Dellerec1a7b32006-12-08 02:40:31 -08001103#define TRANS_MASK_LOW {0x0,0x8,0x4,0xC,0x2,0xA,0x6,0xE,0x1,0x9,0x5,0xD,0x3,0xB,0x7,0xF}
1104#define TRANS_MASK_HIGH {0x000, 0x800, 0x400, 0xC00, 0x200, 0xA00, 0x600, 0xE00, \
1105 0x100, 0x900, 0x500, 0xD00, 0x300, 0xB00, 0x700, 0xF00}
1106
1107#if defined(__LITTLE_ENDIAN)
1108static const u16 transl_l[] = TRANS_MASK_LOW;
1109static const u16 transl_h[] = TRANS_MASK_HIGH;
1110#elif defined(__BIG_ENDIAN)
1111static const u16 transl_l[] = TRANS_MASK_HIGH;
1112static const u16 transl_h[] = TRANS_MASK_LOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113#else
1114#error "Only __BIG_ENDIAN and __LITTLE_ENDIAN are supported in vga-planes"
1115#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117static void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image)
1118{
1119 char oldindex = getindex();
1120 char oldmode = setmode(0x40);
1121 char oldop = setop(0);
1122 char oldsr = setsr(0);
1123 char oldmask = selectmask();
Tetsuo Handabd018a62020-08-31 19:37:00 +09001124 const unsigned char *cdat = image->data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 u32 dx = image->dx;
1126 char __iomem *where;
1127 int y;
1128
1129 dx /= 4;
1130 where = info->screen_base + dx + image->dy * info->fix.line_length;
1131
1132 setmask(0xff);
1133 writeb(image->bg_color, where);
1134 readb(where);
1135 selectmask();
1136 setmask(image->fg_color ^ image->bg_color);
1137 setmode(0x42);
1138 setop(0x18);
1139 for (y = 0; y < image->height; y++, where += info->fix.line_length)
1140 writew(transl_h[cdat[y]&0xF] | transl_l[cdat[y] >> 4], where);
1141 setmask(oldmask);
1142 setsr(oldsr);
1143 setop(oldop);
1144 setmode(oldmode);
1145 setindex(oldindex);
1146}
1147
1148static void vga_imageblit_expand(struct fb_info *info, const struct fb_image *image)
1149{
1150 char __iomem *where = info->screen_base + (image->dx/8) +
1151 image->dy * info->fix.line_length;
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001152 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 char *cdat = (char *) image->data;
1154 char __iomem *dst;
1155 int x, y;
1156
1157 switch (info->fix.type) {
1158 case FB_TYPE_VGA_PLANES:
1159 if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
1160 if (par->isVGA) {
1161 setmode(2);
1162 setop(0);
1163 setsr(0xf);
1164 setcolor(image->fg_color);
1165 selectmask();
1166
1167 setmask(0xff);
1168 writeb(image->bg_color, where);
1169 rmb();
1170 readb(where); /* fill latches */
1171 setmode(3);
1172 wmb();
1173 for (y = 0; y < image->height; y++) {
1174 dst = where;
1175 for (x = image->width/8; x--;)
1176 writeb(*cdat++, dst++);
1177 where += info->fix.line_length;
1178 }
1179 wmb();
1180 } else {
1181 setmode(0);
1182 setop(0);
1183 setsr(0xf);
1184 setcolor(image->bg_color);
1185 selectmask();
1186
1187 setmask(0xff);
1188 for (y = 0; y < image->height; y++) {
1189 dst = where;
1190 for (x=image->width/8; x--;){
1191 rmw(dst);
1192 setcolor(image->fg_color);
1193 selectmask();
1194 if (*cdat) {
1195 setmask(*cdat++);
1196 rmw(dst++);
1197 }
1198 }
1199 where += info->fix.line_length;
1200 }
1201 }
1202 } else
1203 vga_8planes_imageblit(info, image);
1204 break;
1205 case FB_TYPE_PACKED_PIXELS:
1206 default:
1207 cfb_imageblit(info, image);
1208 break;
1209 }
1210}
1211
1212static void vga_imageblit_color(struct fb_info *info, const struct fb_image *image)
1213{
1214 /*
1215 * Draw logo
1216 */
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001217 struct vga16fb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 char __iomem *where =
1219 info->screen_base + image->dy * info->fix.line_length +
1220 image->dx/8;
1221 const char *cdat = image->data;
1222 char __iomem *dst;
1223 int x, y;
1224
1225 switch (info->fix.type) {
1226 case FB_TYPE_VGA_PLANES:
1227 if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4 &&
1228 par->isVGA) {
1229 setsr(0xf);
1230 setop(0);
1231 setmode(0);
1232
1233 for (y = 0; y < image->height; y++) {
1234 for (x = 0; x < image->width; x++) {
1235 dst = where + x/8;
1236
1237 setcolor(*cdat);
1238 selectmask();
1239 setmask(1 << (7 - (x % 8)));
1240 fb_readb(dst);
1241 fb_writeb(0, dst);
1242
1243 cdat++;
1244 }
1245 where += info->fix.line_length;
1246 }
1247 }
1248 break;
1249 case FB_TYPE_PACKED_PIXELS:
1250 cfb_imageblit(info, image);
1251 break;
1252 default:
1253 break;
1254 }
1255}
1256
1257static void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image)
1258{
1259 if (image->depth == 1)
1260 vga_imageblit_expand(info, image);
1261 else
1262 vga_imageblit_color(info, image);
1263}
1264
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001265static void vga16fb_destroy(struct fb_info *info)
1266{
1267 iounmap(info->screen_base);
1268 fb_dealloc_cmap(&info->cmap);
1269 /* XXX unshare VGA regions */
1270 framebuffer_release(info);
1271}
1272
Jani Nikula8a48ac332019-12-03 18:38:50 +02001273static const struct fb_ops vga16fb_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 .owner = THIS_MODULE,
1275 .fb_open = vga16fb_open,
1276 .fb_release = vga16fb_release,
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001277 .fb_destroy = vga16fb_destroy,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 .fb_check_var = vga16fb_check_var,
1279 .fb_set_par = vga16fb_set_par,
1280 .fb_setcolreg = vga16fb_setcolreg,
1281 .fb_pan_display = vga16fb_pan_display,
1282 .fb_blank = vga16fb_blank,
1283 .fb_fillrect = vga16fb_fillrect,
1284 .fb_copyarea = vga16fb_copyarea,
1285 .fb_imageblit = vga16fb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286};
1287
1288#ifndef MODULE
Henrik Kretzschmarad145842010-05-24 14:33:59 -07001289static int __init vga16fb_setup(char *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
1291 char *this_opt;
1292
1293 if (!options || !*options)
1294 return 0;
1295
1296 while ((this_opt = strsep(&options, ",")) != NULL) {
1297 if (!*this_opt) continue;
1298 }
1299 return 0;
1300}
1301#endif
1302
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001303static int vga16fb_probe(struct platform_device *dev)
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001304{
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001305 struct fb_info *info;
1306 struct vga16fb_par *par;
1307 int i;
1308 int ret = 0;
1309
1310 printk(KERN_DEBUG "vga16fb: initializing\n");
1311 info = framebuffer_alloc(sizeof(struct vga16fb_par), &dev->dev);
1312
1313 if (!info) {
1314 ret = -ENOMEM;
1315 goto err_fb_alloc;
1316 }
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001317 info->apertures = alloc_apertures(1);
1318 if (!info->apertures) {
1319 ret = -ENOMEM;
1320 goto err_ioremap;
1321 }
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001322
1323 /* XXX share VGA_FB_PHYS and I/O region with vgacon and others */
Bjorn Helgaas4f1bcaf2006-06-22 14:47:32 -07001324 info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS, 0);
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001325
1326 if (!info->screen_base) {
1327 printk(KERN_ERR "vga16fb: unable to map device\n");
1328 ret = -ENOMEM;
1329 goto err_ioremap;
1330 }
1331
1332 printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base);
1333 par = info->par;
1334
H. Peter Anvin3ea33512007-10-16 22:36:04 -07001335 par->isVGA = screen_info.orig_video_isVGA;
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001336 par->palette_blanked = 0;
1337 par->vesa_blanked = 0;
1338
1339 i = par->isVGA? 6 : 2;
1340
1341 vga16fb_defined.red.length = i;
1342 vga16fb_defined.green.length = i;
1343 vga16fb_defined.blue.length = i;
1344
1345 /* name should not depend on EGA/VGA */
1346 info->fbops = &vga16fb_ops;
1347 info->var = vga16fb_defined;
1348 info->fix = vga16fb_fix;
Antonino A. Daplas7e645ff2007-05-08 00:40:08 -07001349 /* supports rectangles with widths of multiples of 8 */
1350 info->pixmap.blit_x = 1 << 7 | 1 << 15 | 1 << 23 | 1 << 31;
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001351 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE |
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001352 FBINFO_HWACCEL_YPAN;
1353
1354 i = (info->var.bits_per_pixel == 8) ? 256 : 16;
1355 ret = fb_alloc_cmap(&info->cmap, i, 0);
1356 if (ret) {
1357 printk(KERN_ERR "vga16fb: unable to allocate colormap\n");
1358 ret = -ENOMEM;
1359 goto err_alloc_cmap;
1360 }
1361
1362 if (vga16fb_check_var(&info->var, info)) {
1363 printk(KERN_ERR "vga16fb: unable to validate variable\n");
1364 ret = -EINVAL;
1365 goto err_check_var;
1366 }
1367
1368 vga16fb_update_fix(info);
1369
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001370 info->apertures->ranges[0].base = VGA_FB_PHYS;
1371 info->apertures->ranges[0].size = VGA_FB_PHYS_LEN;
1372
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001373 if (register_framebuffer(info) < 0) {
1374 printk(KERN_ERR "vga16fb: unable to register framebuffer\n");
1375 ret = -EINVAL;
1376 goto err_check_var;
1377 }
1378
Joe Perches31b67802013-09-19 18:35:55 -07001379 fb_info(info, "%s frame buffer device\n", info->fix.id);
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001380 platform_set_drvdata(dev, info);
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001381
1382 return 0;
1383
1384 err_check_var:
1385 fb_dealloc_cmap(&info->cmap);
1386 err_alloc_cmap:
1387 iounmap(info->screen_base);
1388 err_ioremap:
1389 framebuffer_release(info);
1390 err_fb_alloc:
1391 return ret;
1392}
1393
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001394static int vga16fb_remove(struct platform_device *dev)
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001395{
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001396 struct fb_info *info = platform_get_drvdata(dev);
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001397
Marcin Slusarz3b9676e2010-05-16 17:33:09 +02001398 if (info)
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001399 unregister_framebuffer(info);
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001400
1401 return 0;
1402}
1403
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001404static struct platform_driver vga16fb_driver = {
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001405 .probe = vga16fb_probe,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001406 .remove = vga16fb_remove,
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001407 .driver = {
1408 .name = "vga16fb",
1409 },
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001410};
1411
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001412static struct platform_device *vga16fb_device;
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001413
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414static int __init vga16fb_init(void)
1415{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 int ret;
1417#ifndef MODULE
1418 char *option = NULL;
1419
1420 if (fb_get_options("vga16fb", &option))
1421 return -ENODEV;
1422
1423 vga16fb_setup(option);
1424#endif
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001425 ret = platform_driver_register(&vga16fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426
Antonino A. Daplas120ddb42005-11-08 21:39:16 -08001427 if (!ret) {
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001428 vga16fb_device = platform_device_alloc("vga16fb", 0);
1429
1430 if (vga16fb_device)
1431 ret = platform_device_add(vga16fb_device);
1432 else
1433 ret = -ENOMEM;
1434
1435 if (ret) {
1436 platform_device_put(vga16fb_device);
1437 platform_driver_unregister(&vga16fb_driver);
1438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 }
1440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 return ret;
1442}
1443
1444static void __exit vga16fb_exit(void)
1445{
Antonino A. Daplasae6d3212006-06-26 00:26:35 -07001446 platform_device_unregister(vga16fb_device);
1447 platform_driver_unregister(&vga16fb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448}
1449
Krzysztof Helt98219372008-07-23 21:31:23 -07001450MODULE_DESCRIPTION("Legacy VGA framebuffer device driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451MODULE_LICENSE("GPL");
1452module_init(vga16fb_init);
1453module_exit(vga16fb_exit);