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