at v3.16-rc2 280 lines 7.1 kB view raw
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 30static 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 37static struct fb_var_screeninfo simplefb_var = { 38 .height = -1, 39 .width = -1, 40 .activate = FB_ACTIVATE_NOW, 41 .vmode = FB_VMODE_NONINTERLACED, 42}; 43 44static 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 69static void simplefb_destroy(struct fb_info *info) 70{ 71 if (info->screen_base) 72 iounmap(info->screen_base); 73} 74 75static 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 84static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 85 86struct simplefb_params { 87 u32 width; 88 u32 height; 89 u32 stride; 90 struct simplefb_format *format; 91}; 92 93static 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", &params->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", &params->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", &params->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 139static 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 166static 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, &params); 179 else if (pdev->dev.of_node) 180 ret = simplefb_parse_dt(pdev, &params); 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 251static 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 261static const struct of_device_id simplefb_of_match[] = { 262 { .compatible = "simple-framebuffer", }, 263 { }, 264}; 265MODULE_DEVICE_TABLE(of, simplefb_of_match); 266 267static 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}; 276module_platform_driver(simplefb_driver); 277 278MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); 279MODULE_DESCRIPTION("Simple framebuffer driver"); 280MODULE_LICENSE("GPL v2");