Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * drivers/video/geode/gx1fb_core.c
0004  *   -- Geode GX1 framebuffer driver
0005  *
0006  * Copyright (C) 2005 Arcom Control Systems Ltd.
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 /* Modes relevant to the GX1 (taken from modedb.c) */
0028 static const struct fb_videomode gx1_modedb[] = {
0029     /* 640x480-60 VESA */
0030     { NULL, 60, 640, 480, 39682,  48, 16, 33, 10, 96, 2,
0031       0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0032     /* 640x480-75 VESA */
0033     { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3,
0034       0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0035     /* 640x480-85 VESA */
0036     { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3,
0037       0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0038     /* 800x600-60 VESA */
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     /* 800x600-75 VESA */
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     /* 800x600-85 VESA */
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     /* 1024x768-60 VESA */
0051     { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
0052       0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
0053     /* 1024x768-75 VESA */
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     /* 1024x768-85 VESA */
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     /* 1280x960-60 VESA */
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     /* 1280x960-85 VESA */
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     /* 1280x1024-60 VESA */
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     /* 1280x1024-75 VESA */
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     /* 1280x1024-85 VESA */
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     /* Maximum resolution is 1280x1024. */
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     /* Only 16 bpp and 8 bpp is supported by the hardware. */
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     /* Enough video memory? */
0122     if (gx1_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len)
0123         return -EINVAL;
0124 
0125     /* FIXME: Check timing parameters here? */
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         /* grayscale = 0.30*R + 0.59*G + 0.11*B */
0161         red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
0162     }
0163 
0164     /* Truecolor has hardware independent palette */
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     /* No HW acceleration for now. */
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     /* Alloc enough space for the pseudo palette. */
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     /* CRT and panel options */
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; /* fall back to CRT if no panel is specified */
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     /* GX1 display controller and CS5530 video device */
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         /* Clear the frame buffer of garbage. */
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");