1*f7018c21STomi Valkeinen /* 2*f7018c21STomi Valkeinen * Simplest possible simple frame-buffer driver, as a platform device 3*f7018c21STomi Valkeinen * 4*f7018c21STomi Valkeinen * Copyright (c) 2013, Stephen Warren 5*f7018c21STomi Valkeinen * 6*f7018c21STomi Valkeinen * Based on q40fb.c, which was: 7*f7018c21STomi Valkeinen * Copyright (C) 2001 Richard Zidlicky <[email protected]> 8*f7018c21STomi Valkeinen * 9*f7018c21STomi Valkeinen * Also based on offb.c, which was: 10*f7018c21STomi Valkeinen * Copyright (C) 1997 Geert Uytterhoeven 11*f7018c21STomi Valkeinen * Copyright (C) 1996 Paul Mackerras 12*f7018c21STomi Valkeinen * 13*f7018c21STomi Valkeinen * This program is free software; you can redistribute it and/or modify it 14*f7018c21STomi Valkeinen * under the terms and conditions of the GNU General Public License, 15*f7018c21STomi Valkeinen * version 2, as published by the Free Software Foundation. 16*f7018c21STomi Valkeinen * 17*f7018c21STomi Valkeinen * This program is distributed in the hope it will be useful, but WITHOUT 18*f7018c21STomi Valkeinen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 19*f7018c21STomi Valkeinen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 20*f7018c21STomi Valkeinen * more details. 21*f7018c21STomi Valkeinen */ 22*f7018c21STomi Valkeinen 23*f7018c21STomi Valkeinen #include <linux/errno.h> 24*f7018c21STomi Valkeinen #include <linux/fb.h> 25*f7018c21STomi Valkeinen #include <linux/io.h> 26*f7018c21STomi Valkeinen #include <linux/module.h> 27*f7018c21STomi Valkeinen #include <linux/platform_data/simplefb.h> 28*f7018c21STomi Valkeinen #include <linux/platform_device.h> 29*f7018c21STomi Valkeinen 30*f7018c21STomi Valkeinen static struct fb_fix_screeninfo simplefb_fix = { 31*f7018c21STomi Valkeinen .id = "simple", 32*f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 33*f7018c21STomi Valkeinen .visual = FB_VISUAL_TRUECOLOR, 34*f7018c21STomi Valkeinen .accel = FB_ACCEL_NONE, 35*f7018c21STomi Valkeinen }; 36*f7018c21STomi Valkeinen 37*f7018c21STomi Valkeinen static struct fb_var_screeninfo simplefb_var = { 38*f7018c21STomi Valkeinen .height = -1, 39*f7018c21STomi Valkeinen .width = -1, 40*f7018c21STomi Valkeinen .activate = FB_ACTIVATE_NOW, 41*f7018c21STomi Valkeinen .vmode = FB_VMODE_NONINTERLACED, 42*f7018c21STomi Valkeinen }; 43*f7018c21STomi Valkeinen 44*f7018c21STomi Valkeinen static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 45*f7018c21STomi Valkeinen u_int transp, struct fb_info *info) 46*f7018c21STomi Valkeinen { 47*f7018c21STomi Valkeinen u32 *pal = info->pseudo_palette; 48*f7018c21STomi Valkeinen u32 cr = red >> (16 - info->var.red.length); 49*f7018c21STomi Valkeinen u32 cg = green >> (16 - info->var.green.length); 50*f7018c21STomi Valkeinen u32 cb = blue >> (16 - info->var.blue.length); 51*f7018c21STomi Valkeinen u32 value; 52*f7018c21STomi Valkeinen 53*f7018c21STomi Valkeinen if (regno >= 16) 54*f7018c21STomi Valkeinen return -EINVAL; 55*f7018c21STomi Valkeinen 56*f7018c21STomi Valkeinen value = (cr << info->var.red.offset) | 57*f7018c21STomi Valkeinen (cg << info->var.green.offset) | 58*f7018c21STomi Valkeinen (cb << info->var.blue.offset); 59*f7018c21STomi Valkeinen if (info->var.transp.length > 0) { 60*f7018c21STomi Valkeinen u32 mask = (1 << info->var.transp.length) - 1; 61*f7018c21STomi Valkeinen mask <<= info->var.transp.offset; 62*f7018c21STomi Valkeinen value |= mask; 63*f7018c21STomi Valkeinen } 64*f7018c21STomi Valkeinen pal[regno] = value; 65*f7018c21STomi Valkeinen 66*f7018c21STomi Valkeinen return 0; 67*f7018c21STomi Valkeinen } 68*f7018c21STomi Valkeinen 69*f7018c21STomi Valkeinen static void simplefb_destroy(struct fb_info *info) 70*f7018c21STomi Valkeinen { 71*f7018c21STomi Valkeinen if (info->screen_base) 72*f7018c21STomi Valkeinen iounmap(info->screen_base); 73*f7018c21STomi Valkeinen } 74*f7018c21STomi Valkeinen 75*f7018c21STomi Valkeinen static struct fb_ops simplefb_ops = { 76*f7018c21STomi Valkeinen .owner = THIS_MODULE, 77*f7018c21STomi Valkeinen .fb_destroy = simplefb_destroy, 78*f7018c21STomi Valkeinen .fb_setcolreg = simplefb_setcolreg, 79*f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect, 80*f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea, 81*f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit, 82*f7018c21STomi Valkeinen }; 83*f7018c21STomi Valkeinen 84*f7018c21STomi Valkeinen static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 85*f7018c21STomi Valkeinen 86*f7018c21STomi Valkeinen struct simplefb_params { 87*f7018c21STomi Valkeinen u32 width; 88*f7018c21STomi Valkeinen u32 height; 89*f7018c21STomi Valkeinen u32 stride; 90*f7018c21STomi Valkeinen struct simplefb_format *format; 91*f7018c21STomi Valkeinen }; 92*f7018c21STomi Valkeinen 93*f7018c21STomi Valkeinen static int simplefb_parse_dt(struct platform_device *pdev, 94*f7018c21STomi Valkeinen struct simplefb_params *params) 95*f7018c21STomi Valkeinen { 96*f7018c21STomi Valkeinen struct device_node *np = pdev->dev.of_node; 97*f7018c21STomi Valkeinen int ret; 98*f7018c21STomi Valkeinen const char *format; 99*f7018c21STomi Valkeinen int i; 100*f7018c21STomi Valkeinen 101*f7018c21STomi Valkeinen ret = of_property_read_u32(np, "width", ¶ms->width); 102*f7018c21STomi Valkeinen if (ret) { 103*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse width property\n"); 104*f7018c21STomi Valkeinen return ret; 105*f7018c21STomi Valkeinen } 106*f7018c21STomi Valkeinen 107*f7018c21STomi Valkeinen ret = of_property_read_u32(np, "height", ¶ms->height); 108*f7018c21STomi Valkeinen if (ret) { 109*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse height property\n"); 110*f7018c21STomi Valkeinen return ret; 111*f7018c21STomi Valkeinen } 112*f7018c21STomi Valkeinen 113*f7018c21STomi Valkeinen ret = of_property_read_u32(np, "stride", ¶ms->stride); 114*f7018c21STomi Valkeinen if (ret) { 115*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse stride property\n"); 116*f7018c21STomi Valkeinen return ret; 117*f7018c21STomi Valkeinen } 118*f7018c21STomi Valkeinen 119*f7018c21STomi Valkeinen ret = of_property_read_string(np, "format", &format); 120*f7018c21STomi Valkeinen if (ret) { 121*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Can't parse format property\n"); 122*f7018c21STomi Valkeinen return ret; 123*f7018c21STomi Valkeinen } 124*f7018c21STomi Valkeinen params->format = NULL; 125*f7018c21STomi Valkeinen for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 126*f7018c21STomi Valkeinen if (strcmp(format, simplefb_formats[i].name)) 127*f7018c21STomi Valkeinen continue; 128*f7018c21STomi Valkeinen params->format = &simplefb_formats[i]; 129*f7018c21STomi Valkeinen break; 130*f7018c21STomi Valkeinen } 131*f7018c21STomi Valkeinen if (!params->format) { 132*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Invalid format value\n"); 133*f7018c21STomi Valkeinen return -EINVAL; 134*f7018c21STomi Valkeinen } 135*f7018c21STomi Valkeinen 136*f7018c21STomi Valkeinen return 0; 137*f7018c21STomi Valkeinen } 138*f7018c21STomi Valkeinen 139*f7018c21STomi Valkeinen static int simplefb_parse_pd(struct platform_device *pdev, 140*f7018c21STomi Valkeinen struct simplefb_params *params) 141*f7018c21STomi Valkeinen { 142*f7018c21STomi Valkeinen struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); 143*f7018c21STomi Valkeinen int i; 144*f7018c21STomi Valkeinen 145*f7018c21STomi Valkeinen params->width = pd->width; 146*f7018c21STomi Valkeinen params->height = pd->height; 147*f7018c21STomi Valkeinen params->stride = pd->stride; 148*f7018c21STomi Valkeinen 149*f7018c21STomi Valkeinen params->format = NULL; 150*f7018c21STomi Valkeinen for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 151*f7018c21STomi Valkeinen if (strcmp(pd->format, simplefb_formats[i].name)) 152*f7018c21STomi Valkeinen continue; 153*f7018c21STomi Valkeinen 154*f7018c21STomi Valkeinen params->format = &simplefb_formats[i]; 155*f7018c21STomi Valkeinen break; 156*f7018c21STomi Valkeinen } 157*f7018c21STomi Valkeinen 158*f7018c21STomi Valkeinen if (!params->format) { 159*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Invalid format value\n"); 160*f7018c21STomi Valkeinen return -EINVAL; 161*f7018c21STomi Valkeinen } 162*f7018c21STomi Valkeinen 163*f7018c21STomi Valkeinen return 0; 164*f7018c21STomi Valkeinen } 165*f7018c21STomi Valkeinen 166*f7018c21STomi Valkeinen static int simplefb_probe(struct platform_device *pdev) 167*f7018c21STomi Valkeinen { 168*f7018c21STomi Valkeinen int ret; 169*f7018c21STomi Valkeinen struct simplefb_params params; 170*f7018c21STomi Valkeinen struct fb_info *info; 171*f7018c21STomi Valkeinen struct resource *mem; 172*f7018c21STomi Valkeinen 173*f7018c21STomi Valkeinen if (fb_get_options("simplefb", NULL)) 174*f7018c21STomi Valkeinen return -ENODEV; 175*f7018c21STomi Valkeinen 176*f7018c21STomi Valkeinen ret = -ENODEV; 177*f7018c21STomi Valkeinen if (dev_get_platdata(&pdev->dev)) 178*f7018c21STomi Valkeinen ret = simplefb_parse_pd(pdev, ¶ms); 179*f7018c21STomi Valkeinen else if (pdev->dev.of_node) 180*f7018c21STomi Valkeinen ret = simplefb_parse_dt(pdev, ¶ms); 181*f7018c21STomi Valkeinen 182*f7018c21STomi Valkeinen if (ret) 183*f7018c21STomi Valkeinen return ret; 184*f7018c21STomi Valkeinen 185*f7018c21STomi Valkeinen mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 186*f7018c21STomi Valkeinen if (!mem) { 187*f7018c21STomi Valkeinen dev_err(&pdev->dev, "No memory resource\n"); 188*f7018c21STomi Valkeinen return -EINVAL; 189*f7018c21STomi Valkeinen } 190*f7018c21STomi Valkeinen 191*f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev); 192*f7018c21STomi Valkeinen if (!info) 193*f7018c21STomi Valkeinen return -ENOMEM; 194*f7018c21STomi Valkeinen platform_set_drvdata(pdev, info); 195*f7018c21STomi Valkeinen 196*f7018c21STomi Valkeinen info->fix = simplefb_fix; 197*f7018c21STomi Valkeinen info->fix.smem_start = mem->start; 198*f7018c21STomi Valkeinen info->fix.smem_len = resource_size(mem); 199*f7018c21STomi Valkeinen info->fix.line_length = params.stride; 200*f7018c21STomi Valkeinen 201*f7018c21STomi Valkeinen info->var = simplefb_var; 202*f7018c21STomi Valkeinen info->var.xres = params.width; 203*f7018c21STomi Valkeinen info->var.yres = params.height; 204*f7018c21STomi Valkeinen info->var.xres_virtual = params.width; 205*f7018c21STomi Valkeinen info->var.yres_virtual = params.height; 206*f7018c21STomi Valkeinen info->var.bits_per_pixel = params.format->bits_per_pixel; 207*f7018c21STomi Valkeinen info->var.red = params.format->red; 208*f7018c21STomi Valkeinen info->var.green = params.format->green; 209*f7018c21STomi Valkeinen info->var.blue = params.format->blue; 210*f7018c21STomi Valkeinen info->var.transp = params.format->transp; 211*f7018c21STomi Valkeinen 212*f7018c21STomi Valkeinen info->apertures = alloc_apertures(1); 213*f7018c21STomi Valkeinen if (!info->apertures) { 214*f7018c21STomi Valkeinen framebuffer_release(info); 215*f7018c21STomi Valkeinen return -ENOMEM; 216*f7018c21STomi Valkeinen } 217*f7018c21STomi Valkeinen info->apertures->ranges[0].base = info->fix.smem_start; 218*f7018c21STomi Valkeinen info->apertures->ranges[0].size = info->fix.smem_len; 219*f7018c21STomi Valkeinen 220*f7018c21STomi Valkeinen info->fbops = &simplefb_ops; 221*f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; 222*f7018c21STomi Valkeinen info->screen_base = ioremap_wc(info->fix.smem_start, 223*f7018c21STomi Valkeinen info->fix.smem_len); 224*f7018c21STomi Valkeinen if (!info->screen_base) { 225*f7018c21STomi Valkeinen framebuffer_release(info); 226*f7018c21STomi Valkeinen return -ENODEV; 227*f7018c21STomi Valkeinen } 228*f7018c21STomi Valkeinen info->pseudo_palette = (void *)(info + 1); 229*f7018c21STomi Valkeinen 230*f7018c21STomi Valkeinen dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", 231*f7018c21STomi Valkeinen info->fix.smem_start, info->fix.smem_len, 232*f7018c21STomi Valkeinen info->screen_base); 233*f7018c21STomi Valkeinen dev_info(&pdev->dev, "format=%s, mode=%dx%dx%d, linelength=%d\n", 234*f7018c21STomi Valkeinen params.format->name, 235*f7018c21STomi Valkeinen info->var.xres, info->var.yres, 236*f7018c21STomi Valkeinen info->var.bits_per_pixel, info->fix.line_length); 237*f7018c21STomi Valkeinen 238*f7018c21STomi Valkeinen ret = register_framebuffer(info); 239*f7018c21STomi Valkeinen if (ret < 0) { 240*f7018c21STomi Valkeinen dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 241*f7018c21STomi Valkeinen iounmap(info->screen_base); 242*f7018c21STomi Valkeinen framebuffer_release(info); 243*f7018c21STomi Valkeinen return ret; 244*f7018c21STomi Valkeinen } 245*f7018c21STomi Valkeinen 246*f7018c21STomi Valkeinen dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 247*f7018c21STomi Valkeinen 248*f7018c21STomi Valkeinen return 0; 249*f7018c21STomi Valkeinen } 250*f7018c21STomi Valkeinen 251*f7018c21STomi Valkeinen static int simplefb_remove(struct platform_device *pdev) 252*f7018c21STomi Valkeinen { 253*f7018c21STomi Valkeinen struct fb_info *info = platform_get_drvdata(pdev); 254*f7018c21STomi Valkeinen 255*f7018c21STomi Valkeinen unregister_framebuffer(info); 256*f7018c21STomi Valkeinen framebuffer_release(info); 257*f7018c21STomi Valkeinen 258*f7018c21STomi Valkeinen return 0; 259*f7018c21STomi Valkeinen } 260*f7018c21STomi Valkeinen 261*f7018c21STomi Valkeinen static const struct of_device_id simplefb_of_match[] = { 262*f7018c21STomi Valkeinen { .compatible = "simple-framebuffer", }, 263*f7018c21STomi Valkeinen { }, 264*f7018c21STomi Valkeinen }; 265*f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, simplefb_of_match); 266*f7018c21STomi Valkeinen 267*f7018c21STomi Valkeinen static struct platform_driver simplefb_driver = { 268*f7018c21STomi Valkeinen .driver = { 269*f7018c21STomi Valkeinen .name = "simple-framebuffer", 270*f7018c21STomi Valkeinen .owner = THIS_MODULE, 271*f7018c21STomi Valkeinen .of_match_table = simplefb_of_match, 272*f7018c21STomi Valkeinen }, 273*f7018c21STomi Valkeinen .probe = simplefb_probe, 274*f7018c21STomi Valkeinen .remove = simplefb_remove, 275*f7018c21STomi Valkeinen }; 276*f7018c21STomi Valkeinen module_platform_driver(simplefb_driver); 277*f7018c21STomi Valkeinen 278*f7018c21STomi Valkeinen MODULE_AUTHOR("Stephen Warren <[email protected]>"); 279*f7018c21STomi Valkeinen MODULE_DESCRIPTION("Simple framebuffer driver"); 280*f7018c21STomi Valkeinen MODULE_LICENSE("GPL v2"); 281