at v3.12-rc2 264 lines 6.6 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 struct fb_ops simplefb_ops = { 70 .owner = THIS_MODULE, 71 .fb_setcolreg = simplefb_setcolreg, 72 .fb_fillrect = cfb_fillrect, 73 .fb_copyarea = cfb_copyarea, 74 .fb_imageblit = cfb_imageblit, 75}; 76 77static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; 78 79struct simplefb_params { 80 u32 width; 81 u32 height; 82 u32 stride; 83 struct simplefb_format *format; 84}; 85 86static int simplefb_parse_dt(struct platform_device *pdev, 87 struct simplefb_params *params) 88{ 89 struct device_node *np = pdev->dev.of_node; 90 int ret; 91 const char *format; 92 int i; 93 94 ret = of_property_read_u32(np, "width", &params->width); 95 if (ret) { 96 dev_err(&pdev->dev, "Can't parse width property\n"); 97 return ret; 98 } 99 100 ret = of_property_read_u32(np, "height", &params->height); 101 if (ret) { 102 dev_err(&pdev->dev, "Can't parse height property\n"); 103 return ret; 104 } 105 106 ret = of_property_read_u32(np, "stride", &params->stride); 107 if (ret) { 108 dev_err(&pdev->dev, "Can't parse stride property\n"); 109 return ret; 110 } 111 112 ret = of_property_read_string(np, "format", &format); 113 if (ret) { 114 dev_err(&pdev->dev, "Can't parse format property\n"); 115 return ret; 116 } 117 params->format = NULL; 118 for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 119 if (strcmp(format, simplefb_formats[i].name)) 120 continue; 121 params->format = &simplefb_formats[i]; 122 break; 123 } 124 if (!params->format) { 125 dev_err(&pdev->dev, "Invalid format value\n"); 126 return -EINVAL; 127 } 128 129 return 0; 130} 131 132static int simplefb_parse_pd(struct platform_device *pdev, 133 struct simplefb_params *params) 134{ 135 struct simplefb_platform_data *pd = pdev->dev.platform_data; 136 int i; 137 138 params->width = pd->width; 139 params->height = pd->height; 140 params->stride = pd->stride; 141 142 params->format = NULL; 143 for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 144 if (strcmp(pd->format, simplefb_formats[i].name)) 145 continue; 146 147 params->format = &simplefb_formats[i]; 148 break; 149 } 150 151 if (!params->format) { 152 dev_err(&pdev->dev, "Invalid format value\n"); 153 return -EINVAL; 154 } 155 156 return 0; 157} 158 159static int simplefb_probe(struct platform_device *pdev) 160{ 161 int ret; 162 struct simplefb_params params; 163 struct fb_info *info; 164 struct resource *mem; 165 166 if (fb_get_options("simplefb", NULL)) 167 return -ENODEV; 168 169 ret = -ENODEV; 170 if (pdev->dev.platform_data) 171 ret = simplefb_parse_pd(pdev, &params); 172 else if (pdev->dev.of_node) 173 ret = simplefb_parse_dt(pdev, &params); 174 175 if (ret) 176 return ret; 177 178 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 179 if (!mem) { 180 dev_err(&pdev->dev, "No memory resource\n"); 181 return -EINVAL; 182 } 183 184 info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev); 185 if (!info) 186 return -ENOMEM; 187 platform_set_drvdata(pdev, info); 188 189 info->fix = simplefb_fix; 190 info->fix.smem_start = mem->start; 191 info->fix.smem_len = resource_size(mem); 192 info->fix.line_length = params.stride; 193 194 info->var = simplefb_var; 195 info->var.xres = params.width; 196 info->var.yres = params.height; 197 info->var.xres_virtual = params.width; 198 info->var.yres_virtual = params.height; 199 info->var.bits_per_pixel = params.format->bits_per_pixel; 200 info->var.red = params.format->red; 201 info->var.green = params.format->green; 202 info->var.blue = params.format->blue; 203 info->var.transp = params.format->transp; 204 205 info->apertures = alloc_apertures(1); 206 if (!info->apertures) { 207 framebuffer_release(info); 208 return -ENOMEM; 209 } 210 info->apertures->ranges[0].base = info->fix.smem_start; 211 info->apertures->ranges[0].size = info->fix.smem_len; 212 213 info->fbops = &simplefb_ops; 214 info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; 215 info->screen_base = devm_ioremap(&pdev->dev, info->fix.smem_start, 216 info->fix.smem_len); 217 if (!info->screen_base) { 218 framebuffer_release(info); 219 return -ENODEV; 220 } 221 info->pseudo_palette = (void *)(info + 1); 222 223 ret = register_framebuffer(info); 224 if (ret < 0) { 225 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 226 framebuffer_release(info); 227 return ret; 228 } 229 230 dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 231 232 return 0; 233} 234 235static int simplefb_remove(struct platform_device *pdev) 236{ 237 struct fb_info *info = platform_get_drvdata(pdev); 238 239 unregister_framebuffer(info); 240 framebuffer_release(info); 241 242 return 0; 243} 244 245static const struct of_device_id simplefb_of_match[] = { 246 { .compatible = "simple-framebuffer", }, 247 { }, 248}; 249MODULE_DEVICE_TABLE(of, simplefb_of_match); 250 251static struct platform_driver simplefb_driver = { 252 .driver = { 253 .name = "simple-framebuffer", 254 .owner = THIS_MODULE, 255 .of_match_table = simplefb_of_match, 256 }, 257 .probe = simplefb_probe, 258 .remove = simplefb_remove, 259}; 260module_platform_driver(simplefb_driver); 261 262MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); 263MODULE_DESCRIPTION("Simple framebuffer driver"); 264MODULE_LICENSE("GPL v2");