0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/module.h>
0018 #include <linux/kernel.h>
0019 #include <linux/errno.h>
0020 #include <linux/string.h>
0021 #include <linux/ctype.h>
0022 #include <linux/mm.h>
0023 #include <linux/init.h>
0024 #include <linux/fb.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/dma-mapping.h>
0027 #include <linux/io.h>
0028 #include <linux/gfp.h>
0029
0030 #include <mach/hardware.h>
0031 #include <asm/irq.h>
0032 #include <asm/mach-types.h>
0033
0034 #include "acornfb.h"
0035
0036
0037
0038
0039
0040
0041 #define DEFAULT_XRES 640
0042 #define DEFAULT_YRES 480
0043 #define DEFAULT_BPP 4
0044
0045
0046
0047
0048 #undef DEBUG_MODE_SELECTION
0049
0050
0051
0052
0053
0054
0055
0056 #define NR_MONTYPES 6
0057 static struct fb_monspecs monspecs[NR_MONTYPES] = {
0058 {
0059 .hfmin = 15469,
0060 .hfmax = 15781,
0061 .vfmin = 49,
0062 .vfmax = 51,
0063 }, {
0064 .hfmin = 0,
0065 .hfmax = 99999,
0066 .vfmin = 0,
0067 .vfmax = 199,
0068 }, {
0069 .hfmin = 58608,
0070 .hfmax = 58608,
0071 .vfmin = 64,
0072 .vfmax = 64,
0073 }, {
0074 .hfmin = 30000,
0075 .hfmax = 70000,
0076 .vfmin = 60,
0077 .vfmax = 60,
0078 }, {
0079 .hfmin = 30000,
0080 .hfmax = 70000,
0081 .vfmin = 56,
0082 .vfmax = 75,
0083 }, {
0084 .hfmin = 30000,
0085 .hfmax = 70000,
0086 .vfmin = 60,
0087 .vfmax = 60,
0088 }
0089 };
0090
0091 static struct fb_info fb_info;
0092 static struct acornfb_par current_par;
0093 static struct vidc_timing current_vidc;
0094
0095 extern unsigned int vram_size;
0096
0097 #ifdef HAS_VIDC20
0098 #include <mach/acornfb.h>
0099
0100 #define MAX_SIZE (2*1024*1024)
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112 static void acornfb_set_timing(struct fb_info *info)
0113 {
0114 struct fb_var_screeninfo *var = &info->var;
0115 struct vidc_timing vidc;
0116 u_int vcr, fsize;
0117 u_int ext_ctl, dat_ctl;
0118 u_int words_per_line;
0119
0120 memset(&vidc, 0, sizeof(vidc));
0121
0122 vidc.h_sync_width = var->hsync_len - 8;
0123 vidc.h_border_start = vidc.h_sync_width + var->left_margin + 8 - 12;
0124 vidc.h_display_start = vidc.h_border_start + 12 - 18;
0125 vidc.h_display_end = vidc.h_display_start + var->xres;
0126 vidc.h_border_end = vidc.h_display_end + 18 - 12;
0127 vidc.h_cycle = vidc.h_border_end + var->right_margin + 12 - 8;
0128 vidc.h_interlace = vidc.h_cycle / 2;
0129 vidc.v_sync_width = var->vsync_len - 1;
0130 vidc.v_border_start = vidc.v_sync_width + var->upper_margin;
0131 vidc.v_display_start = vidc.v_border_start;
0132 vidc.v_display_end = vidc.v_display_start + var->yres;
0133 vidc.v_border_end = vidc.v_display_end;
0134 vidc.control = acornfb_default_control();
0135
0136 vcr = var->vsync_len + var->upper_margin + var->yres +
0137 var->lower_margin;
0138
0139 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
0140 vidc.v_cycle = (vcr - 3) / 2;
0141 vidc.control |= VIDC20_CTRL_INT;
0142 } else
0143 vidc.v_cycle = vcr - 2;
0144
0145 switch (var->bits_per_pixel) {
0146 case 1: vidc.control |= VIDC20_CTRL_1BPP; break;
0147 case 2: vidc.control |= VIDC20_CTRL_2BPP; break;
0148 case 4: vidc.control |= VIDC20_CTRL_4BPP; break;
0149 default:
0150 case 8: vidc.control |= VIDC20_CTRL_8BPP; break;
0151 case 16: vidc.control |= VIDC20_CTRL_16BPP; break;
0152 case 32: vidc.control |= VIDC20_CTRL_32BPP; break;
0153 }
0154
0155 acornfb_vidc20_find_rates(&vidc, var);
0156 fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
0157
0158 if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) {
0159 current_vidc = vidc;
0160
0161 vidc_writel(VIDC20_CTRL | vidc.control);
0162 vidc_writel(0xd0000000 | vidc.pll_ctl);
0163 vidc_writel(0x80000000 | vidc.h_cycle);
0164 vidc_writel(0x81000000 | vidc.h_sync_width);
0165 vidc_writel(0x82000000 | vidc.h_border_start);
0166 vidc_writel(0x83000000 | vidc.h_display_start);
0167 vidc_writel(0x84000000 | vidc.h_display_end);
0168 vidc_writel(0x85000000 | vidc.h_border_end);
0169 vidc_writel(0x86000000);
0170 vidc_writel(0x87000000 | vidc.h_interlace);
0171 vidc_writel(0x90000000 | vidc.v_cycle);
0172 vidc_writel(0x91000000 | vidc.v_sync_width);
0173 vidc_writel(0x92000000 | vidc.v_border_start);
0174 vidc_writel(0x93000000 | vidc.v_display_start);
0175 vidc_writel(0x94000000 | vidc.v_display_end);
0176 vidc_writel(0x95000000 | vidc.v_border_end);
0177 vidc_writel(0x96000000);
0178 vidc_writel(0x97000000);
0179 }
0180
0181 iomd_writel(fsize, IOMD_FSIZE);
0182
0183 ext_ctl = acornfb_default_econtrol();
0184
0185 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
0186 ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
0187 else {
0188 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
0189 ext_ctl |= VIDC20_ECTL_HS_HSYNC;
0190 else
0191 ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
0192
0193 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
0194 ext_ctl |= VIDC20_ECTL_VS_VSYNC;
0195 else
0196 ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
0197 }
0198
0199 vidc_writel(VIDC20_ECTL | ext_ctl);
0200
0201 words_per_line = var->xres * var->bits_per_pixel / 32;
0202
0203 if (current_par.using_vram && info->fix.smem_len == 2048*1024)
0204 words_per_line /= 2;
0205
0206
0207 dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
0208
0209
0210
0211
0212
0213
0214
0215 if (current_par.using_vram && current_par.vram_half_sam == 2048)
0216 dat_ctl |= VIDC20_DCTL_BUS_D63_0;
0217 else
0218 dat_ctl |= VIDC20_DCTL_BUS_D31_0;
0219
0220 vidc_writel(VIDC20_DCTL | dat_ctl);
0221
0222 #ifdef DEBUG_MODE_SELECTION
0223 printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
0224 var->yres, var->bits_per_pixel);
0225 printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle);
0226 printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width);
0227 printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start);
0228 printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start);
0229 printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end);
0230 printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end);
0231 printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace);
0232 printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle);
0233 printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width);
0234 printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start);
0235 printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start);
0236 printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end);
0237 printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end);
0238 printk(KERN_DEBUG " Ext Ctrl (C) : 0x%08X\n", ext_ctl);
0239 printk(KERN_DEBUG " PLL Ctrl (D) : 0x%08X\n", vidc.pll_ctl);
0240 printk(KERN_DEBUG " Ctrl (E) : 0x%08X\n", vidc.control);
0241 printk(KERN_DEBUG " Data Ctrl (F) : 0x%08X\n", dat_ctl);
0242 printk(KERN_DEBUG " Fsize : 0x%08X\n", fsize);
0243 #endif
0244 }
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 static int
0265 acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
0266 u_int trans, struct fb_info *info)
0267 {
0268 union palette pal;
0269
0270 if (regno >= current_par.palette_size)
0271 return 1;
0272
0273 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
0274 u32 pseudo_val;
0275
0276 pseudo_val = regno << info->var.red.offset;
0277 pseudo_val |= regno << info->var.green.offset;
0278 pseudo_val |= regno << info->var.blue.offset;
0279
0280 ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
0281 }
0282
0283 pal.p = 0;
0284 pal.vidc20.red = red >> 8;
0285 pal.vidc20.green = green >> 8;
0286 pal.vidc20.blue = blue >> 8;
0287
0288 current_par.palette[regno] = pal;
0289
0290 if (info->var.bits_per_pixel == 16) {
0291 int i;
0292
0293 pal.p = 0;
0294 vidc_writel(0x10000000);
0295 for (i = 0; i < 256; i += 1) {
0296 pal.vidc20.red = current_par.palette[i & 31].vidc20.red;
0297 pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
0298 pal.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue;
0299 vidc_writel(pal.p);
0300
0301 }
0302 } else {
0303 vidc_writel(0x10000000 | regno);
0304 vidc_writel(pal.p);
0305 }
0306
0307 return 0;
0308 }
0309 #endif
0310
0311
0312
0313
0314
0315 static int
0316 acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
0317 {
0318 u_int font_line_len, sam_size, min_size, size, nr_y;
0319
0320
0321 var->xres = (var->xres + 1) & ~1;
0322
0323
0324
0325
0326 var->xres_virtual = var->xres;
0327 var->xoffset = 0;
0328
0329 if (current_par.using_vram)
0330 sam_size = current_par.vram_half_sam * 2;
0331 else
0332 sam_size = 16;
0333
0334
0335
0336
0337
0338
0339
0340
0341 font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
0342 min_size = var->xres * var->yres * var->bits_per_pixel / 8;
0343
0344
0345
0346
0347
0348 if (min_size > info->fix.smem_len)
0349 return -EINVAL;
0350
0351
0352
0353
0354 for (size = info->fix.smem_len;
0355 nr_y = size / font_line_len, min_size <= size;
0356 size -= sam_size) {
0357 if (nr_y * font_line_len == size)
0358 break;
0359 }
0360 nr_y *= fontht;
0361
0362 if (var->accel_flags & FB_ACCELF_TEXT) {
0363 if (min_size > size) {
0364
0365
0366
0367 size = info->fix.smem_len;
0368 var->yres_virtual = size / (font_line_len / fontht);
0369 } else
0370 var->yres_virtual = nr_y;
0371 } else if (var->yres_virtual > nr_y)
0372 var->yres_virtual = nr_y;
0373
0374 current_par.screen_end = info->fix.smem_start + size;
0375
0376
0377
0378
0379 if (var->yres > var->yres_virtual)
0380 var->yres = var->yres_virtual;
0381
0382 if (var->vmode & FB_VMODE_YWRAP) {
0383 if (var->yoffset > var->yres_virtual)
0384 var->yoffset = var->yres_virtual;
0385 } else {
0386 if (var->yoffset + var->yres > var->yres_virtual)
0387 var->yoffset = var->yres_virtual - var->yres;
0388 }
0389
0390
0391 var->hsync_len = (var->hsync_len + 1) & ~1;
0392
0393 #if defined(HAS_VIDC20)
0394
0395 if (var->left_margin & 1) {
0396 var->left_margin += 1;
0397 var->right_margin -= 1;
0398 }
0399
0400
0401 if (var->right_margin & 1)
0402 var->right_margin += 1;
0403 #endif
0404
0405 if (var->vsync_len < 1)
0406 var->vsync_len = 1;
0407
0408 return 0;
0409 }
0410
0411 static int
0412 acornfb_validate_timing(struct fb_var_screeninfo *var,
0413 struct fb_monspecs *monspecs)
0414 {
0415 unsigned long hs, vs;
0416
0417
0418
0419
0420
0421
0422
0423
0424 hs = 1953125000 / var->pixclock;
0425 hs = hs * 512 /
0426 (var->xres + var->left_margin + var->right_margin + var->hsync_len);
0427 vs = hs /
0428 (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
0429
0430 return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
0431 hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
0432 }
0433
0434 static inline void
0435 acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
0436 {
0437 u_int off = var->yoffset * info->fix.line_length;
0438
0439 #if defined(HAS_MEMC)
0440 memc_write(VDMA_INIT, off >> 2);
0441 #elif defined(HAS_IOMD)
0442 iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
0443 #endif
0444 }
0445
0446 static int
0447 acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
0448 {
0449 u_int fontht;
0450 int err;
0451
0452
0453
0454
0455 fontht = 8;
0456
0457 var->red.msb_right = 0;
0458 var->green.msb_right = 0;
0459 var->blue.msb_right = 0;
0460 var->transp.msb_right = 0;
0461
0462 switch (var->bits_per_pixel) {
0463 case 1: case 2: case 4: case 8:
0464 var->red.offset = 0;
0465 var->red.length = var->bits_per_pixel;
0466 var->green = var->red;
0467 var->blue = var->red;
0468 var->transp.offset = 0;
0469 var->transp.length = 0;
0470 break;
0471
0472 #ifdef HAS_VIDC20
0473 case 16:
0474 var->red.offset = 0;
0475 var->red.length = 5;
0476 var->green.offset = 5;
0477 var->green.length = 5;
0478 var->blue.offset = 10;
0479 var->blue.length = 5;
0480 var->transp.offset = 15;
0481 var->transp.length = 1;
0482 break;
0483
0484 case 32:
0485 var->red.offset = 0;
0486 var->red.length = 8;
0487 var->green.offset = 8;
0488 var->green.length = 8;
0489 var->blue.offset = 16;
0490 var->blue.length = 8;
0491 var->transp.offset = 24;
0492 var->transp.length = 4;
0493 break;
0494 #endif
0495 default:
0496 return -EINVAL;
0497 }
0498
0499
0500
0501
0502 if (!acornfb_valid_pixrate(var))
0503 return -EINVAL;
0504
0505
0506
0507
0508
0509 err = acornfb_adjust_timing(info, var, fontht);
0510 if (err)
0511 return err;
0512
0513
0514
0515
0516
0517 return acornfb_validate_timing(var, &info->monspecs);
0518 }
0519
0520 static int acornfb_set_par(struct fb_info *info)
0521 {
0522 switch (info->var.bits_per_pixel) {
0523 case 1:
0524 current_par.palette_size = 2;
0525 info->fix.visual = FB_VISUAL_MONO10;
0526 break;
0527 case 2:
0528 current_par.palette_size = 4;
0529 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0530 break;
0531 case 4:
0532 current_par.palette_size = 16;
0533 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0534 break;
0535 case 8:
0536 current_par.palette_size = VIDC_PALETTE_SIZE;
0537 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0538 break;
0539 #ifdef HAS_VIDC20
0540 case 16:
0541 current_par.palette_size = 32;
0542 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
0543 break;
0544 case 32:
0545 current_par.palette_size = VIDC_PALETTE_SIZE;
0546 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
0547 break;
0548 #endif
0549 default:
0550 BUG();
0551 }
0552
0553 info->fix.line_length = (info->var.xres * info->var.bits_per_pixel) / 8;
0554
0555 #if defined(HAS_MEMC)
0556 {
0557 unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
0558
0559 memc_write(VDMA_START, 0);
0560 memc_write(VDMA_END, size >> 2);
0561 }
0562 #elif defined(HAS_IOMD)
0563 {
0564 unsigned long start, size;
0565 u_int control;
0566
0567 start = info->fix.smem_start;
0568 size = current_par.screen_end;
0569
0570 if (current_par.using_vram) {
0571 size -= current_par.vram_half_sam;
0572 control = DMA_CR_E | (current_par.vram_half_sam / 256);
0573 } else {
0574 size -= 16;
0575 control = DMA_CR_E | DMA_CR_D | 16;
0576 }
0577
0578 iomd_writel(start, IOMD_VIDSTART);
0579 iomd_writel(size, IOMD_VIDEND);
0580 iomd_writel(control, IOMD_VIDCR);
0581 }
0582 #endif
0583
0584 acornfb_update_dma(info, &info->var);
0585 acornfb_set_timing(info);
0586
0587 return 0;
0588 }
0589
0590 static int
0591 acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
0592 {
0593 u_int y_bottom = var->yoffset;
0594
0595 if (!(var->vmode & FB_VMODE_YWRAP))
0596 y_bottom += info->var.yres;
0597
0598 if (y_bottom > info->var.yres_virtual)
0599 return -EINVAL;
0600
0601 acornfb_update_dma(info, var);
0602
0603 return 0;
0604 }
0605
0606 static const struct fb_ops acornfb_ops = {
0607 .owner = THIS_MODULE,
0608 .fb_check_var = acornfb_check_var,
0609 .fb_set_par = acornfb_set_par,
0610 .fb_setcolreg = acornfb_setcolreg,
0611 .fb_pan_display = acornfb_pan_display,
0612 .fb_fillrect = cfb_fillrect,
0613 .fb_copyarea = cfb_copyarea,
0614 .fb_imageblit = cfb_imageblit,
0615 };
0616
0617
0618
0619
0620 static struct fb_videomode modedb[] = {
0621 {
0622 NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
0623 FB_SYNC_COMP_HIGH_ACT,
0624 FB_VMODE_NONINTERLACED
0625 }, {
0626 NULL, 50, 640, 250, 62500, 185, 123, 38, 21, 76, 3,
0627 0,
0628 FB_VMODE_NONINTERLACED
0629 }, {
0630 NULL, 50, 640, 256, 62500, 185, 123, 35, 18, 76, 3,
0631 0,
0632 FB_VMODE_NONINTERLACED
0633 }, {
0634 NULL, 50, 640, 512, 41667, 113, 87, 18, 1, 56, 3,
0635 0,
0636 FB_VMODE_NONINTERLACED
0637 }, {
0638 NULL, 70, 640, 250, 39722, 48, 16, 109, 88, 96, 2,
0639 0,
0640 FB_VMODE_NONINTERLACED
0641 }, {
0642 NULL, 70, 640, 256, 39722, 48, 16, 106, 85, 96, 2,
0643 0,
0644 FB_VMODE_NONINTERLACED
0645 }, {
0646 NULL, 70, 640, 352, 39722, 48, 16, 58, 37, 96, 2,
0647 0,
0648 FB_VMODE_NONINTERLACED
0649 }, {
0650 NULL, 60, 640, 480, 39722, 48, 16, 32, 11, 96, 2,
0651 0,
0652 FB_VMODE_NONINTERLACED
0653 }, {
0654 NULL, 56, 800, 600, 27778, 101, 23, 22, 1, 100, 2,
0655 0,
0656 FB_VMODE_NONINTERLACED
0657 }, {
0658 NULL, 60, 896, 352, 41667, 59, 27, 9, 0, 118, 3,
0659 0,
0660 FB_VMODE_NONINTERLACED
0661 }, {
0662 NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
0663 0,
0664 FB_VMODE_NONINTERLACED
0665 }, {
0666 NULL, 60, 1280, 1024, 9090, 186, 96, 38, 1, 160, 3,
0667 0,
0668 FB_VMODE_NONINTERLACED
0669 }
0670 };
0671
0672 static struct fb_videomode acornfb_default_mode = {
0673 .name = NULL,
0674 .refresh = 60,
0675 .xres = 640,
0676 .yres = 480,
0677 .pixclock = 39722,
0678 .left_margin = 56,
0679 .right_margin = 16,
0680 .upper_margin = 34,
0681 .lower_margin = 9,
0682 .hsync_len = 88,
0683 .vsync_len = 2,
0684 .sync = 0,
0685 .vmode = FB_VMODE_NONINTERLACED
0686 };
0687
0688 static void acornfb_init_fbinfo(void)
0689 {
0690 static int first = 1;
0691
0692 if (!first)
0693 return;
0694 first = 0;
0695
0696 fb_info.fbops = &acornfb_ops;
0697 fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
0698 fb_info.pseudo_palette = current_par.pseudo_palette;
0699
0700 strcpy(fb_info.fix.id, "Acorn");
0701 fb_info.fix.type = FB_TYPE_PACKED_PIXELS;
0702 fb_info.fix.type_aux = 0;
0703 fb_info.fix.xpanstep = 0;
0704 fb_info.fix.ypanstep = 1;
0705 fb_info.fix.ywrapstep = 1;
0706 fb_info.fix.line_length = 0;
0707 fb_info.fix.accel = FB_ACCEL_NONE;
0708
0709
0710
0711
0712 memset(&fb_info.var, 0, sizeof(fb_info.var));
0713
0714 #if defined(HAS_VIDC20)
0715 fb_info.var.red.length = 8;
0716 fb_info.var.transp.length = 4;
0717 #endif
0718 fb_info.var.green = fb_info.var.red;
0719 fb_info.var.blue = fb_info.var.red;
0720 fb_info.var.nonstd = 0;
0721 fb_info.var.activate = FB_ACTIVATE_NOW;
0722 fb_info.var.height = -1;
0723 fb_info.var.width = -1;
0724 fb_info.var.vmode = FB_VMODE_NONINTERLACED;
0725 fb_info.var.accel_flags = FB_ACCELF_TEXT;
0726
0727 current_par.dram_size = 0;
0728 current_par.montype = -1;
0729 current_par.dpms = 0;
0730 }
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740
0741
0742
0743
0744
0745
0746
0747
0748
0749
0750
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761 static void acornfb_parse_mon(char *opt)
0762 {
0763 char *p = opt;
0764
0765 current_par.montype = -2;
0766
0767 fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
0768 if (*p == '-')
0769 fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
0770 else
0771 fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
0772
0773 if (*p != ':')
0774 goto bad;
0775
0776 fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
0777 if (*p == '-')
0778 fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
0779 else
0780 fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
0781
0782 if (*p != ':')
0783 goto check_values;
0784
0785 fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
0786
0787 if (*p != ':')
0788 goto check_values;
0789
0790 fb_info.var.width = simple_strtoul(p + 1, &p, 0);
0791
0792 if (*p != ':')
0793 goto check_values;
0794
0795 fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
0796
0797 check_values:
0798 if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
0799 fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
0800 goto bad;
0801 return;
0802
0803 bad:
0804 printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
0805 current_par.montype = -1;
0806 }
0807
0808 static void acornfb_parse_montype(char *opt)
0809 {
0810 current_par.montype = -2;
0811
0812 if (strncmp(opt, "tv", 2) == 0) {
0813 opt += 2;
0814 current_par.montype = 0;
0815 } else if (strncmp(opt, "multi", 5) == 0) {
0816 opt += 5;
0817 current_par.montype = 1;
0818 } else if (strncmp(opt, "hires", 5) == 0) {
0819 opt += 5;
0820 current_par.montype = 2;
0821 } else if (strncmp(opt, "vga", 3) == 0) {
0822 opt += 3;
0823 current_par.montype = 3;
0824 } else if (strncmp(opt, "svga", 4) == 0) {
0825 opt += 4;
0826 current_par.montype = 4;
0827 } else if (strncmp(opt, "auto", 4) == 0) {
0828 opt += 4;
0829 current_par.montype = -1;
0830 } else if (isdigit(*opt))
0831 current_par.montype = simple_strtoul(opt, &opt, 0);
0832
0833 if (current_par.montype == -2 ||
0834 current_par.montype > NR_MONTYPES) {
0835 printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
0836 opt);
0837 current_par.montype = -1;
0838 } else
0839 if (opt && *opt) {
0840 if (strcmp(opt, ",dpms") == 0)
0841 current_par.dpms = 1;
0842 else
0843 printk(KERN_ERR
0844 "acornfb: unknown monitor option: %s\n",
0845 opt);
0846 }
0847 }
0848
0849 static void acornfb_parse_dram(char *opt)
0850 {
0851 unsigned int size;
0852
0853 size = simple_strtoul(opt, &opt, 0);
0854
0855 if (opt) {
0856 switch (*opt) {
0857 case 'M':
0858 case 'm':
0859 size *= 1024;
0860 fallthrough;
0861 case 'K':
0862 case 'k':
0863 size *= 1024;
0864 default:
0865 break;
0866 }
0867 }
0868
0869 current_par.dram_size = size;
0870 }
0871
0872 static struct options {
0873 char *name;
0874 void (*parse)(char *opt);
0875 } opt_table[] = {
0876 { "mon", acornfb_parse_mon },
0877 { "montype", acornfb_parse_montype },
0878 { "dram", acornfb_parse_dram },
0879 { NULL, NULL }
0880 };
0881
0882 static int acornfb_setup(char *options)
0883 {
0884 struct options *optp;
0885 char *opt;
0886
0887 if (!options || !*options)
0888 return 0;
0889
0890 acornfb_init_fbinfo();
0891
0892 while ((opt = strsep(&options, ",")) != NULL) {
0893 if (!*opt)
0894 continue;
0895
0896 for (optp = opt_table; optp->name; optp++) {
0897 int optlen;
0898
0899 optlen = strlen(optp->name);
0900
0901 if (strncmp(opt, optp->name, optlen) == 0 &&
0902 opt[optlen] == ':') {
0903 optp->parse(opt + optlen + 1);
0904 break;
0905 }
0906 }
0907
0908 if (!optp->name)
0909 printk(KERN_ERR "acornfb: unknown parameter: %s\n",
0910 opt);
0911 }
0912 return 0;
0913 }
0914
0915
0916
0917
0918
0919 static int acornfb_detect_monitortype(void)
0920 {
0921 return 4;
0922 }
0923
0924 static int acornfb_probe(struct platform_device *dev)
0925 {
0926 unsigned long size;
0927 u_int h_sync, v_sync;
0928 int rc, i;
0929 char *option = NULL;
0930
0931 if (fb_get_options("acornfb", &option))
0932 return -ENODEV;
0933 acornfb_setup(option);
0934
0935 acornfb_init_fbinfo();
0936
0937 current_par.dev = &dev->dev;
0938
0939 if (current_par.montype == -1)
0940 current_par.montype = acornfb_detect_monitortype();
0941
0942 if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
0943 current_par.montype = 4;
0944
0945 if (current_par.montype >= 0) {
0946 fb_info.monspecs = monspecs[current_par.montype];
0947 fb_info.monspecs.dpms = current_par.dpms;
0948 }
0949
0950
0951
0952
0953 for (i = 0; i < ARRAY_SIZE(modedb); i++) {
0954 unsigned long hs;
0955
0956 hs = modedb[i].refresh *
0957 (modedb[i].yres + modedb[i].upper_margin +
0958 modedb[i].lower_margin + modedb[i].vsync_len);
0959 if (modedb[i].xres == DEFAULT_XRES &&
0960 modedb[i].yres == DEFAULT_YRES &&
0961 modedb[i].refresh >= fb_info.monspecs.vfmin &&
0962 modedb[i].refresh <= fb_info.monspecs.vfmax &&
0963 hs >= fb_info.monspecs.hfmin &&
0964 hs <= fb_info.monspecs.hfmax) {
0965 acornfb_default_mode = modedb[i];
0966 break;
0967 }
0968 }
0969
0970 fb_info.screen_base = (char *)SCREEN_BASE;
0971 fb_info.fix.smem_start = SCREEN_START;
0972 current_par.using_vram = 0;
0973
0974
0975
0976
0977
0978
0979 if (vram_size && !current_par.dram_size) {
0980 size = vram_size;
0981 current_par.vram_half_sam = vram_size / 1024;
0982 current_par.using_vram = 1;
0983 } else if (current_par.dram_size)
0984 size = current_par.dram_size;
0985 else
0986 size = MAX_SIZE;
0987
0988
0989
0990
0991 if (size > MAX_SIZE)
0992 size = MAX_SIZE;
0993
0994 size = PAGE_ALIGN(size);
0995
0996 #if defined(HAS_VIDC20)
0997 if (!current_par.using_vram) {
0998 dma_addr_t handle;
0999 void *base;
1000
1001
1002
1003
1004
1005
1006 base = dma_alloc_wc(current_par.dev, size, &handle,
1007 GFP_KERNEL);
1008 if (base == NULL) {
1009 printk(KERN_ERR "acornfb: unable to allocate screen memory\n");
1010 return -ENOMEM;
1011 }
1012
1013 fb_info.screen_base = base;
1014 fb_info.fix.smem_start = handle;
1015 }
1016 #endif
1017 fb_info.fix.smem_len = size;
1018 current_par.palette_size = VIDC_PALETTE_SIZE;
1019
1020
1021
1022
1023
1024
1025 do {
1026 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1027 ARRAY_SIZE(modedb),
1028 &acornfb_default_mode, DEFAULT_BPP);
1029
1030
1031
1032 if (rc == 1)
1033 break;
1034
1035 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1036 &acornfb_default_mode, DEFAULT_BPP);
1037
1038
1039
1040 if (rc == 1)
1041 break;
1042
1043 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
1044 ARRAY_SIZE(modedb),
1045 &acornfb_default_mode, DEFAULT_BPP);
1046 if (rc)
1047 break;
1048
1049 rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
1050 &acornfb_default_mode, DEFAULT_BPP);
1051 } while (0);
1052
1053
1054
1055
1056
1057 if (rc == 0) {
1058 printk("Acornfb: no valid mode found\n");
1059 return -EINVAL;
1060 }
1061
1062 h_sync = 1953125000 / fb_info.var.pixclock;
1063 h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
1064 fb_info.var.right_margin + fb_info.var.hsync_len);
1065 v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
1066 fb_info.var.lower_margin + fb_info.var.vsync_len);
1067
1068 printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, %d.%03dkHz, %dHz\n",
1069 fb_info.fix.smem_len / 1024,
1070 current_par.using_vram ? 'V' : 'D',
1071 VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
1072 h_sync / 1000, h_sync % 1000, v_sync);
1073
1074 printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
1075 fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
1076 fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
1077 fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
1078 fb_info.monspecs.dpms ? ", DPMS" : "");
1079
1080 if (fb_set_var(&fb_info, &fb_info.var))
1081 printk(KERN_ERR "Acornfb: unable to set display parameters\n");
1082
1083 if (register_framebuffer(&fb_info) < 0)
1084 return -EINVAL;
1085 return 0;
1086 }
1087
1088 static struct platform_driver acornfb_driver = {
1089 .probe = acornfb_probe,
1090 .driver = {
1091 .name = "acornfb",
1092 },
1093 };
1094
1095 static int __init acornfb_init(void)
1096 {
1097 return platform_driver_register(&acornfb_driver);
1098 }
1099
1100 module_init(acornfb_init);
1101
1102 MODULE_AUTHOR("Russell King");
1103 MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
1104 MODULE_LICENSE("GPL");