0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/kernel.h>
0011 #include <linux/errno.h>
0012 #include <linux/string.h>
0013 #include <linux/mm.h>
0014 #include <linux/delay.h>
0015 #include <linux/fb.h>
0016 #include <linux/init.h>
0017 #include <linux/pci.h>
0018
0019 #include "geodefb.h"
0020 #include "display_gx1.h"
0021 #include "video_cs5530.h"
0022
0023 static char mode_option[32] = "640x480-16@60";
0024 static int crt_option = 1;
0025 static char panel_option[32] = "";
0026
0027
0028 static const struct fb_videomode gx1_modedb[] = {
0029
0030 { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2,
0031 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0032
0033 { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
0034 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0035
0036 { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
0037 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0038
0039 { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4,
0040 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0041 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0042
0043 { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3,
0044 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0045 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0046
0047 { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3,
0048 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0049 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0050
0051 { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
0052 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0053
0054 { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
0055 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0056 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0057
0058 { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
0059 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0060 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0061
0062 { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
0063 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0064 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0065
0066 { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
0067 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0068 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0069
0070 { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
0071 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0072 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0073
0074 { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
0075 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0076 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0077
0078 { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
0079 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0080 FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0081 };
0082
0083 static int gx1_line_delta(int xres, int bpp)
0084 {
0085 int line_delta = xres * (bpp >> 3);
0086
0087 if (line_delta > 2048)
0088 line_delta = 4096;
0089 else if (line_delta > 1024)
0090 line_delta = 2048;
0091 else
0092 line_delta = 1024;
0093 return line_delta;
0094 }
0095
0096 static int gx1fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
0097 {
0098 struct geodefb_par *par = info->par;
0099
0100
0101 if (var->xres > 1280 || var->yres > 1024)
0102 return -EINVAL;
0103
0104 if (par->panel_x && (var->xres > par->panel_x || var->yres > par->panel_y))
0105 return -EINVAL;
0106
0107
0108 if (var->bits_per_pixel == 16) {
0109 var->red.offset = 11; var->red.length = 5;
0110 var->green.offset = 5; var->green.length = 6;
0111 var->blue.offset = 0; var->blue.length = 5;
0112 var->transp.offset = 0; var->transp.length = 0;
0113 } else if (var->bits_per_pixel == 8) {
0114 var->red.offset = 0; var->red.length = 8;
0115 var->green.offset = 0; var->green.length = 8;
0116 var->blue.offset = 0; var->blue.length = 8;
0117 var->transp.offset = 0; var->transp.length = 0;
0118 } else
0119 return -EINVAL;
0120
0121
0122 if (gx1_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len)
0123 return -EINVAL;
0124
0125
0126
0127 return 0;
0128 }
0129
0130 static int gx1fb_set_par(struct fb_info *info)
0131 {
0132 struct geodefb_par *par = info->par;
0133
0134 if (info->var.bits_per_pixel == 16)
0135 info->fix.visual = FB_VISUAL_TRUECOLOR;
0136 else
0137 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0138
0139 info->fix.line_length = gx1_line_delta(info->var.xres, info->var.bits_per_pixel);
0140
0141 par->dc_ops->set_mode(info);
0142
0143 return 0;
0144 }
0145
0146 static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
0147 {
0148 chan &= 0xffff;
0149 chan >>= 16 - bf->length;
0150 return chan << bf->offset;
0151 }
0152
0153 static int gx1fb_setcolreg(unsigned regno, unsigned red, unsigned green,
0154 unsigned blue, unsigned transp,
0155 struct fb_info *info)
0156 {
0157 struct geodefb_par *par = info->par;
0158
0159 if (info->var.grayscale) {
0160
0161 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
0162 }
0163
0164
0165 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
0166 u32 *pal = info->pseudo_palette;
0167 u32 v;
0168
0169 if (regno >= 16)
0170 return -EINVAL;
0171
0172 v = chan_to_field(red, &info->var.red);
0173 v |= chan_to_field(green, &info->var.green);
0174 v |= chan_to_field(blue, &info->var.blue);
0175
0176 pal[regno] = v;
0177 } else {
0178 if (regno >= 256)
0179 return -EINVAL;
0180
0181 par->dc_ops->set_palette_reg(info, regno, red, green, blue);
0182 }
0183
0184 return 0;
0185 }
0186
0187 static int gx1fb_blank(int blank_mode, struct fb_info *info)
0188 {
0189 struct geodefb_par *par = info->par;
0190
0191 return par->vid_ops->blank_display(info, blank_mode);
0192 }
0193
0194 static int gx1fb_map_video_memory(struct fb_info *info, struct pci_dev *dev)
0195 {
0196 struct geodefb_par *par = info->par;
0197 unsigned gx_base;
0198 int fb_len;
0199 int ret;
0200
0201 gx_base = gx1_gx_base();
0202 if (!gx_base)
0203 return -ENODEV;
0204
0205 ret = pci_enable_device(dev);
0206 if (ret < 0)
0207 return ret;
0208
0209 ret = pci_request_region(dev, 0, "gx1fb (video)");
0210 if (ret < 0)
0211 return ret;
0212 par->vid_regs = pci_ioremap_bar(dev, 0);
0213 if (!par->vid_regs)
0214 return -ENOMEM;
0215
0216 if (!request_mem_region(gx_base + 0x8300, 0x100, "gx1fb (display controller)"))
0217 return -EBUSY;
0218 par->dc_regs = ioremap(gx_base + 0x8300, 0x100);
0219 if (!par->dc_regs)
0220 return -ENOMEM;
0221
0222 if ((fb_len = gx1_frame_buffer_size()) < 0)
0223 return -ENOMEM;
0224 info->fix.smem_start = gx_base + 0x800000;
0225 info->fix.smem_len = fb_len;
0226 info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
0227 if (!info->screen_base)
0228 return -ENOMEM;
0229
0230 dev_info(&dev->dev, "%d Kibyte of video memory at 0x%lx\n",
0231 info->fix.smem_len / 1024, info->fix.smem_start);
0232
0233 return 0;
0234 }
0235
0236 static int parse_panel_option(struct fb_info *info)
0237 {
0238 struct geodefb_par *par = info->par;
0239
0240 if (strcmp(panel_option, "") != 0) {
0241 int x, y;
0242 char *s;
0243 x = simple_strtol(panel_option, &s, 10);
0244 if (!x)
0245 return -EINVAL;
0246 y = simple_strtol(s + 1, NULL, 10);
0247 if (!y)
0248 return -EINVAL;
0249 par->panel_x = x;
0250 par->panel_y = y;
0251 }
0252 return 0;
0253 }
0254
0255 static const struct fb_ops gx1fb_ops = {
0256 .owner = THIS_MODULE,
0257 .fb_check_var = gx1fb_check_var,
0258 .fb_set_par = gx1fb_set_par,
0259 .fb_setcolreg = gx1fb_setcolreg,
0260 .fb_blank = gx1fb_blank,
0261
0262 .fb_fillrect = cfb_fillrect,
0263 .fb_copyarea = cfb_copyarea,
0264 .fb_imageblit = cfb_imageblit,
0265 };
0266
0267 static struct fb_info *gx1fb_init_fbinfo(struct device *dev)
0268 {
0269 struct geodefb_par *par;
0270 struct fb_info *info;
0271
0272
0273 info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, dev);
0274 if (!info)
0275 return NULL;
0276
0277 par = info->par;
0278
0279 strcpy(info->fix.id, "GX1");
0280
0281 info->fix.type = FB_TYPE_PACKED_PIXELS;
0282 info->fix.type_aux = 0;
0283 info->fix.xpanstep = 0;
0284 info->fix.ypanstep = 0;
0285 info->fix.ywrapstep = 0;
0286 info->fix.accel = FB_ACCEL_NONE;
0287
0288 info->var.nonstd = 0;
0289 info->var.activate = FB_ACTIVATE_NOW;
0290 info->var.height = -1;
0291 info->var.width = -1;
0292 info->var.accel_flags = 0;
0293 info->var.vmode = FB_VMODE_NONINTERLACED;
0294
0295 info->fbops = &gx1fb_ops;
0296 info->flags = FBINFO_DEFAULT;
0297 info->node = -1;
0298
0299 info->pseudo_palette = (void *)par + sizeof(struct geodefb_par);
0300
0301 info->var.grayscale = 0;
0302
0303
0304 par->enable_crt = crt_option;
0305 if (parse_panel_option(info) < 0)
0306 printk(KERN_WARNING "gx1fb: invalid 'panel' option -- disabling flat panel\n");
0307 if (!par->panel_x)
0308 par->enable_crt = 1;
0309
0310 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
0311 framebuffer_release(info);
0312 return NULL;
0313 }
0314 return info;
0315 }
0316
0317 static int gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0318 {
0319 struct geodefb_par *par;
0320 struct fb_info *info;
0321 int ret;
0322
0323 info = gx1fb_init_fbinfo(&pdev->dev);
0324 if (!info)
0325 return -ENOMEM;
0326 par = info->par;
0327
0328
0329 par->dc_ops = &gx1_dc_ops;
0330 par->vid_ops = &cs5530_vid_ops;
0331
0332 if ((ret = gx1fb_map_video_memory(info, pdev)) < 0) {
0333 dev_err(&pdev->dev, "failed to map frame buffer or controller registers\n");
0334 goto err;
0335 }
0336
0337 ret = fb_find_mode(&info->var, info, mode_option,
0338 gx1_modedb, ARRAY_SIZE(gx1_modedb), NULL, 16);
0339 if (ret == 0 || ret == 4) {
0340 dev_err(&pdev->dev, "could not find valid video mode\n");
0341 ret = -EINVAL;
0342 goto err;
0343 }
0344
0345
0346 memset_io(info->screen_base, 0, info->fix.smem_len);
0347
0348 gx1fb_check_var(&info->var, info);
0349 gx1fb_set_par(info);
0350
0351 if (register_framebuffer(info) < 0) {
0352 ret = -EINVAL;
0353 goto err;
0354 }
0355 pci_set_drvdata(pdev, info);
0356 fb_info(info, "%s frame buffer device\n", info->fix.id);
0357 return 0;
0358
0359 err:
0360 if (info->screen_base) {
0361 iounmap(info->screen_base);
0362 pci_release_region(pdev, 0);
0363 }
0364 if (par->vid_regs) {
0365 iounmap(par->vid_regs);
0366 pci_release_region(pdev, 1);
0367 }
0368 if (par->dc_regs) {
0369 iounmap(par->dc_regs);
0370 release_mem_region(gx1_gx_base() + 0x8300, 0x100);
0371 }
0372
0373 fb_dealloc_cmap(&info->cmap);
0374 framebuffer_release(info);
0375
0376 return ret;
0377 }
0378
0379 static void gx1fb_remove(struct pci_dev *pdev)
0380 {
0381 struct fb_info *info = pci_get_drvdata(pdev);
0382 struct geodefb_par *par = info->par;
0383
0384 unregister_framebuffer(info);
0385
0386 iounmap((void __iomem *)info->screen_base);
0387 pci_release_region(pdev, 0);
0388
0389 iounmap(par->vid_regs);
0390 pci_release_region(pdev, 1);
0391
0392 iounmap(par->dc_regs);
0393 release_mem_region(gx1_gx_base() + 0x8300, 0x100);
0394
0395 fb_dealloc_cmap(&info->cmap);
0396
0397 framebuffer_release(info);
0398 }
0399
0400 #ifndef MODULE
0401 static void __init gx1fb_setup(char *options)
0402 {
0403 char *this_opt;
0404
0405 if (!options || !*options)
0406 return;
0407
0408 while ((this_opt = strsep(&options, ","))) {
0409 if (!*this_opt)
0410 continue;
0411
0412 if (!strncmp(this_opt, "mode:", 5))
0413 strscpy(mode_option, this_opt + 5, sizeof(mode_option));
0414 else if (!strncmp(this_opt, "crt:", 4))
0415 crt_option = !!simple_strtoul(this_opt + 4, NULL, 0);
0416 else if (!strncmp(this_opt, "panel:", 6))
0417 strscpy(panel_option, this_opt + 6, sizeof(panel_option));
0418 else
0419 strscpy(mode_option, this_opt, sizeof(mode_option));
0420 }
0421 }
0422 #endif
0423
0424 static struct pci_device_id gx1fb_id_table[] = {
0425 { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_VIDEO,
0426 PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
0427 0xff0000, 0 },
0428 { 0, }
0429 };
0430
0431 MODULE_DEVICE_TABLE(pci, gx1fb_id_table);
0432
0433 static struct pci_driver gx1fb_driver = {
0434 .name = "gx1fb",
0435 .id_table = gx1fb_id_table,
0436 .probe = gx1fb_probe,
0437 .remove = gx1fb_remove,
0438 };
0439
0440 static int __init gx1fb_init(void)
0441 {
0442 #ifndef MODULE
0443 char *option = NULL;
0444
0445 if (fb_get_options("gx1fb", &option))
0446 return -ENODEV;
0447 gx1fb_setup(option);
0448 #endif
0449 return pci_register_driver(&gx1fb_driver);
0450 }
0451
0452 static void gx1fb_cleanup(void)
0453 {
0454 pci_unregister_driver(&gx1fb_driver);
0455 }
0456
0457 module_init(gx1fb_init);
0458 module_exit(gx1fb_cleanup);
0459
0460 module_param_string(mode, mode_option, sizeof(mode_option), 0444);
0461 MODULE_PARM_DESC(mode, "video mode (<x>x<y>[-<bpp>][@<refr>])");
0462
0463 module_param_named(crt, crt_option, int, 0444);
0464 MODULE_PARM_DESC(crt, "enable CRT output. 0 = off, 1 = on (default)");
0465
0466 module_param_string(panel, panel_option, sizeof(panel_option), 0444);
0467 MODULE_PARM_DESC(panel, "size of attached flat panel (<x>x<y>)");
0468
0469 MODULE_DESCRIPTION("framebuffer driver for the AMD Geode GX1");
0470 MODULE_LICENSE("GPL");