1*f7018c21STomi Valkeinen /* sunxvr1000.c: Sun XVR-1000 driver for sparc64 systems 2*f7018c21STomi Valkeinen * 3*f7018c21STomi Valkeinen * Copyright (C) 2010 David S. Miller (davem@davemloft.net) 4*f7018c21STomi Valkeinen */ 5*f7018c21STomi Valkeinen 6*f7018c21STomi Valkeinen #include <linux/module.h> 7*f7018c21STomi Valkeinen #include <linux/kernel.h> 8*f7018c21STomi Valkeinen #include <linux/fb.h> 9*f7018c21STomi Valkeinen #include <linux/init.h> 10*f7018c21STomi Valkeinen #include <linux/of_device.h> 11*f7018c21STomi Valkeinen 12*f7018c21STomi Valkeinen struct gfb_info { 13*f7018c21STomi Valkeinen struct fb_info *info; 14*f7018c21STomi Valkeinen 15*f7018c21STomi Valkeinen char __iomem *fb_base; 16*f7018c21STomi Valkeinen unsigned long fb_base_phys; 17*f7018c21STomi Valkeinen 18*f7018c21STomi Valkeinen struct device_node *of_node; 19*f7018c21STomi Valkeinen 20*f7018c21STomi Valkeinen unsigned int width; 21*f7018c21STomi Valkeinen unsigned int height; 22*f7018c21STomi Valkeinen unsigned int depth; 23*f7018c21STomi Valkeinen unsigned int fb_size; 24*f7018c21STomi Valkeinen 25*f7018c21STomi Valkeinen u32 pseudo_palette[16]; 26*f7018c21STomi Valkeinen }; 27*f7018c21STomi Valkeinen 28*f7018c21STomi Valkeinen static int gfb_get_props(struct gfb_info *gp) 29*f7018c21STomi Valkeinen { 30*f7018c21STomi Valkeinen gp->width = of_getintprop_default(gp->of_node, "width", 0); 31*f7018c21STomi Valkeinen gp->height = of_getintprop_default(gp->of_node, "height", 0); 32*f7018c21STomi Valkeinen gp->depth = of_getintprop_default(gp->of_node, "depth", 32); 33*f7018c21STomi Valkeinen 34*f7018c21STomi Valkeinen if (!gp->width || !gp->height) { 35*f7018c21STomi Valkeinen printk(KERN_ERR "gfb: Critical properties missing for %s\n", 36*f7018c21STomi Valkeinen gp->of_node->full_name); 37*f7018c21STomi Valkeinen return -EINVAL; 38*f7018c21STomi Valkeinen } 39*f7018c21STomi Valkeinen 40*f7018c21STomi Valkeinen return 0; 41*f7018c21STomi Valkeinen } 42*f7018c21STomi Valkeinen 43*f7018c21STomi Valkeinen static int gfb_setcolreg(unsigned regno, 44*f7018c21STomi Valkeinen unsigned red, unsigned green, unsigned blue, 45*f7018c21STomi Valkeinen unsigned transp, struct fb_info *info) 46*f7018c21STomi Valkeinen { 47*f7018c21STomi Valkeinen u32 value; 48*f7018c21STomi Valkeinen 49*f7018c21STomi Valkeinen if (regno < 16) { 50*f7018c21STomi Valkeinen red >>= 8; 51*f7018c21STomi Valkeinen green >>= 8; 52*f7018c21STomi Valkeinen blue >>= 8; 53*f7018c21STomi Valkeinen 54*f7018c21STomi Valkeinen value = (blue << 16) | (green << 8) | red; 55*f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[regno] = value; 56*f7018c21STomi Valkeinen } 57*f7018c21STomi Valkeinen 58*f7018c21STomi Valkeinen return 0; 59*f7018c21STomi Valkeinen } 60*f7018c21STomi Valkeinen 61*f7018c21STomi Valkeinen static struct fb_ops gfb_ops = { 62*f7018c21STomi Valkeinen .owner = THIS_MODULE, 63*f7018c21STomi Valkeinen .fb_setcolreg = gfb_setcolreg, 64*f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect, 65*f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea, 66*f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit, 67*f7018c21STomi Valkeinen }; 68*f7018c21STomi Valkeinen 69*f7018c21STomi Valkeinen static int gfb_set_fbinfo(struct gfb_info *gp) 70*f7018c21STomi Valkeinen { 71*f7018c21STomi Valkeinen struct fb_info *info = gp->info; 72*f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 73*f7018c21STomi Valkeinen 74*f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT; 75*f7018c21STomi Valkeinen info->fbops = &gfb_ops; 76*f7018c21STomi Valkeinen info->screen_base = gp->fb_base; 77*f7018c21STomi Valkeinen info->screen_size = gp->fb_size; 78*f7018c21STomi Valkeinen 79*f7018c21STomi Valkeinen info->pseudo_palette = gp->pseudo_palette; 80*f7018c21STomi Valkeinen 81*f7018c21STomi Valkeinen /* Fill fix common fields */ 82*f7018c21STomi Valkeinen strlcpy(info->fix.id, "gfb", sizeof(info->fix.id)); 83*f7018c21STomi Valkeinen info->fix.smem_start = gp->fb_base_phys; 84*f7018c21STomi Valkeinen info->fix.smem_len = gp->fb_size; 85*f7018c21STomi Valkeinen info->fix.type = FB_TYPE_PACKED_PIXELS; 86*f7018c21STomi Valkeinen if (gp->depth == 32 || gp->depth == 24) 87*f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR; 88*f7018c21STomi Valkeinen else 89*f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 90*f7018c21STomi Valkeinen 91*f7018c21STomi Valkeinen var->xres = gp->width; 92*f7018c21STomi Valkeinen var->yres = gp->height; 93*f7018c21STomi Valkeinen var->xres_virtual = var->xres; 94*f7018c21STomi Valkeinen var->yres_virtual = var->yres; 95*f7018c21STomi Valkeinen var->bits_per_pixel = gp->depth; 96*f7018c21STomi Valkeinen 97*f7018c21STomi Valkeinen var->red.offset = 0; 98*f7018c21STomi Valkeinen var->red.length = 8; 99*f7018c21STomi Valkeinen var->green.offset = 8; 100*f7018c21STomi Valkeinen var->green.length = 8; 101*f7018c21STomi Valkeinen var->blue.offset = 16; 102*f7018c21STomi Valkeinen var->blue.length = 8; 103*f7018c21STomi Valkeinen var->transp.offset = 0; 104*f7018c21STomi Valkeinen var->transp.length = 0; 105*f7018c21STomi Valkeinen 106*f7018c21STomi Valkeinen if (fb_alloc_cmap(&info->cmap, 256, 0)) { 107*f7018c21STomi Valkeinen printk(KERN_ERR "gfb: Cannot allocate color map.\n"); 108*f7018c21STomi Valkeinen return -ENOMEM; 109*f7018c21STomi Valkeinen } 110*f7018c21STomi Valkeinen 111*f7018c21STomi Valkeinen return 0; 112*f7018c21STomi Valkeinen } 113*f7018c21STomi Valkeinen 114*f7018c21STomi Valkeinen static int gfb_probe(struct platform_device *op) 115*f7018c21STomi Valkeinen { 116*f7018c21STomi Valkeinen struct device_node *dp = op->dev.of_node; 117*f7018c21STomi Valkeinen struct fb_info *info; 118*f7018c21STomi Valkeinen struct gfb_info *gp; 119*f7018c21STomi Valkeinen int err; 120*f7018c21STomi Valkeinen 121*f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct gfb_info), &op->dev); 122*f7018c21STomi Valkeinen if (!info) { 123*f7018c21STomi Valkeinen printk(KERN_ERR "gfb: Cannot allocate fb_info\n"); 124*f7018c21STomi Valkeinen err = -ENOMEM; 125*f7018c21STomi Valkeinen goto err_out; 126*f7018c21STomi Valkeinen } 127*f7018c21STomi Valkeinen 128*f7018c21STomi Valkeinen gp = info->par; 129*f7018c21STomi Valkeinen gp->info = info; 130*f7018c21STomi Valkeinen gp->of_node = dp; 131*f7018c21STomi Valkeinen 132*f7018c21STomi Valkeinen gp->fb_base_phys = op->resource[6].start; 133*f7018c21STomi Valkeinen 134*f7018c21STomi Valkeinen err = gfb_get_props(gp); 135*f7018c21STomi Valkeinen if (err) 136*f7018c21STomi Valkeinen goto err_release_fb; 137*f7018c21STomi Valkeinen 138*f7018c21STomi Valkeinen /* Framebuffer length is the same regardless of resolution. */ 139*f7018c21STomi Valkeinen info->fix.line_length = 16384; 140*f7018c21STomi Valkeinen gp->fb_size = info->fix.line_length * gp->height; 141*f7018c21STomi Valkeinen 142*f7018c21STomi Valkeinen gp->fb_base = of_ioremap(&op->resource[6], 0, 143*f7018c21STomi Valkeinen gp->fb_size, "gfb fb"); 144*f7018c21STomi Valkeinen if (!gp->fb_base) { 145*f7018c21STomi Valkeinen err = -ENOMEM; 146*f7018c21STomi Valkeinen goto err_release_fb; 147*f7018c21STomi Valkeinen } 148*f7018c21STomi Valkeinen 149*f7018c21STomi Valkeinen err = gfb_set_fbinfo(gp); 150*f7018c21STomi Valkeinen if (err) 151*f7018c21STomi Valkeinen goto err_unmap_fb; 152*f7018c21STomi Valkeinen 153*f7018c21STomi Valkeinen printk("gfb: Found device at %s\n", dp->full_name); 154*f7018c21STomi Valkeinen 155*f7018c21STomi Valkeinen err = register_framebuffer(info); 156*f7018c21STomi Valkeinen if (err < 0) { 157*f7018c21STomi Valkeinen printk(KERN_ERR "gfb: Could not register framebuffer %s\n", 158*f7018c21STomi Valkeinen dp->full_name); 159*f7018c21STomi Valkeinen goto err_unmap_fb; 160*f7018c21STomi Valkeinen } 161*f7018c21STomi Valkeinen 162*f7018c21STomi Valkeinen dev_set_drvdata(&op->dev, info); 163*f7018c21STomi Valkeinen 164*f7018c21STomi Valkeinen return 0; 165*f7018c21STomi Valkeinen 166*f7018c21STomi Valkeinen err_unmap_fb: 167*f7018c21STomi Valkeinen of_iounmap(&op->resource[6], gp->fb_base, gp->fb_size); 168*f7018c21STomi Valkeinen 169*f7018c21STomi Valkeinen err_release_fb: 170*f7018c21STomi Valkeinen framebuffer_release(info); 171*f7018c21STomi Valkeinen 172*f7018c21STomi Valkeinen err_out: 173*f7018c21STomi Valkeinen return err; 174*f7018c21STomi Valkeinen } 175*f7018c21STomi Valkeinen 176*f7018c21STomi Valkeinen static int gfb_remove(struct platform_device *op) 177*f7018c21STomi Valkeinen { 178*f7018c21STomi Valkeinen struct fb_info *info = dev_get_drvdata(&op->dev); 179*f7018c21STomi Valkeinen struct gfb_info *gp = info->par; 180*f7018c21STomi Valkeinen 181*f7018c21STomi Valkeinen unregister_framebuffer(info); 182*f7018c21STomi Valkeinen 183*f7018c21STomi Valkeinen iounmap(gp->fb_base); 184*f7018c21STomi Valkeinen 185*f7018c21STomi Valkeinen of_iounmap(&op->resource[6], gp->fb_base, gp->fb_size); 186*f7018c21STomi Valkeinen 187*f7018c21STomi Valkeinen framebuffer_release(info); 188*f7018c21STomi Valkeinen 189*f7018c21STomi Valkeinen return 0; 190*f7018c21STomi Valkeinen } 191*f7018c21STomi Valkeinen 192*f7018c21STomi Valkeinen static const struct of_device_id gfb_match[] = { 193*f7018c21STomi Valkeinen { 194*f7018c21STomi Valkeinen .name = "SUNW,gfb", 195*f7018c21STomi Valkeinen }, 196*f7018c21STomi Valkeinen {}, 197*f7018c21STomi Valkeinen }; 198*f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, ffb_match); 199*f7018c21STomi Valkeinen 200*f7018c21STomi Valkeinen static struct platform_driver gfb_driver = { 201*f7018c21STomi Valkeinen .probe = gfb_probe, 202*f7018c21STomi Valkeinen .remove = gfb_remove, 203*f7018c21STomi Valkeinen .driver = { 204*f7018c21STomi Valkeinen .name = "gfb", 205*f7018c21STomi Valkeinen .owner = THIS_MODULE, 206*f7018c21STomi Valkeinen .of_match_table = gfb_match, 207*f7018c21STomi Valkeinen }, 208*f7018c21STomi Valkeinen }; 209*f7018c21STomi Valkeinen 210*f7018c21STomi Valkeinen static int __init gfb_init(void) 211*f7018c21STomi Valkeinen { 212*f7018c21STomi Valkeinen if (fb_get_options("gfb", NULL)) 213*f7018c21STomi Valkeinen return -ENODEV; 214*f7018c21STomi Valkeinen 215*f7018c21STomi Valkeinen return platform_driver_register(&gfb_driver); 216*f7018c21STomi Valkeinen } 217*f7018c21STomi Valkeinen 218*f7018c21STomi Valkeinen static void __exit gfb_exit(void) 219*f7018c21STomi Valkeinen { 220*f7018c21STomi Valkeinen platform_driver_unregister(&gfb_driver); 221*f7018c21STomi Valkeinen } 222*f7018c21STomi Valkeinen 223*f7018c21STomi Valkeinen module_init(gfb_init); 224*f7018c21STomi Valkeinen module_exit(gfb_exit); 225*f7018c21STomi Valkeinen 226*f7018c21STomi Valkeinen MODULE_DESCRIPTION("framebuffer driver for Sun XVR-1000 graphics"); 227*f7018c21STomi Valkeinen MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); 228*f7018c21STomi Valkeinen MODULE_VERSION("1.0"); 229*f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 230