at v3.11-rc2 234 lines 5.8 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_device.h> 28 29static struct fb_fix_screeninfo simplefb_fix = { 30 .id = "simple", 31 .type = FB_TYPE_PACKED_PIXELS, 32 .visual = FB_VISUAL_TRUECOLOR, 33 .accel = FB_ACCEL_NONE, 34}; 35 36static struct fb_var_screeninfo simplefb_var = { 37 .height = -1, 38 .width = -1, 39 .activate = FB_ACTIVATE_NOW, 40 .vmode = FB_VMODE_NONINTERLACED, 41}; 42 43static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 44 u_int transp, struct fb_info *info) 45{ 46 u32 *pal = info->pseudo_palette; 47 u32 cr = red >> (16 - info->var.red.length); 48 u32 cg = green >> (16 - info->var.green.length); 49 u32 cb = blue >> (16 - info->var.blue.length); 50 u32 value; 51 52 if (regno >= 16) 53 return -EINVAL; 54 55 value = (cr << info->var.red.offset) | 56 (cg << info->var.green.offset) | 57 (cb << info->var.blue.offset); 58 if (info->var.transp.length > 0) { 59 u32 mask = (1 << info->var.transp.length) - 1; 60 mask <<= info->var.transp.offset; 61 value |= mask; 62 } 63 pal[regno] = value; 64 65 return 0; 66} 67 68static struct fb_ops simplefb_ops = { 69 .owner = THIS_MODULE, 70 .fb_setcolreg = simplefb_setcolreg, 71 .fb_fillrect = cfb_fillrect, 72 .fb_copyarea = cfb_copyarea, 73 .fb_imageblit = cfb_imageblit, 74}; 75 76struct simplefb_format { 77 const char *name; 78 u32 bits_per_pixel; 79 struct fb_bitfield red; 80 struct fb_bitfield green; 81 struct fb_bitfield blue; 82 struct fb_bitfield transp; 83}; 84 85static struct simplefb_format simplefb_formats[] = { 86 { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} }, 87}; 88 89struct simplefb_params { 90 u32 width; 91 u32 height; 92 u32 stride; 93 struct simplefb_format *format; 94}; 95 96static int simplefb_parse_dt(struct platform_device *pdev, 97 struct simplefb_params *params) 98{ 99 struct device_node *np = pdev->dev.of_node; 100 int ret; 101 const char *format; 102 int i; 103 104 ret = of_property_read_u32(np, "width", &params->width); 105 if (ret) { 106 dev_err(&pdev->dev, "Can't parse width property\n"); 107 return ret; 108 } 109 110 ret = of_property_read_u32(np, "height", &params->height); 111 if (ret) { 112 dev_err(&pdev->dev, "Can't parse height property\n"); 113 return ret; 114 } 115 116 ret = of_property_read_u32(np, "stride", &params->stride); 117 if (ret) { 118 dev_err(&pdev->dev, "Can't parse stride property\n"); 119 return ret; 120 } 121 122 ret = of_property_read_string(np, "format", &format); 123 if (ret) { 124 dev_err(&pdev->dev, "Can't parse format property\n"); 125 return ret; 126 } 127 params->format = NULL; 128 for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { 129 if (strcmp(format, simplefb_formats[i].name)) 130 continue; 131 params->format = &simplefb_formats[i]; 132 break; 133 } 134 if (!params->format) { 135 dev_err(&pdev->dev, "Invalid format value\n"); 136 return -EINVAL; 137 } 138 139 return 0; 140} 141 142static int simplefb_probe(struct platform_device *pdev) 143{ 144 int ret; 145 struct simplefb_params params; 146 struct fb_info *info; 147 struct resource *mem; 148 149 if (fb_get_options("simplefb", NULL)) 150 return -ENODEV; 151 152 ret = simplefb_parse_dt(pdev, &params); 153 if (ret) 154 return ret; 155 156 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 157 if (!mem) { 158 dev_err(&pdev->dev, "No memory resource\n"); 159 return -EINVAL; 160 } 161 162 info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev); 163 if (!info) 164 return -ENOMEM; 165 platform_set_drvdata(pdev, info); 166 167 info->fix = simplefb_fix; 168 info->fix.smem_start = mem->start; 169 info->fix.smem_len = resource_size(mem); 170 info->fix.line_length = params.stride; 171 172 info->var = simplefb_var; 173 info->var.xres = params.width; 174 info->var.yres = params.height; 175 info->var.xres_virtual = params.width; 176 info->var.yres_virtual = params.height; 177 info->var.bits_per_pixel = params.format->bits_per_pixel; 178 info->var.red = params.format->red; 179 info->var.green = params.format->green; 180 info->var.blue = params.format->blue; 181 info->var.transp = params.format->transp; 182 183 info->fbops = &simplefb_ops; 184 info->flags = FBINFO_DEFAULT; 185 info->screen_base = devm_ioremap(&pdev->dev, info->fix.smem_start, 186 info->fix.smem_len); 187 if (!info->screen_base) { 188 framebuffer_release(info); 189 return -ENODEV; 190 } 191 info->pseudo_palette = (void *)(info + 1); 192 193 ret = register_framebuffer(info); 194 if (ret < 0) { 195 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 196 framebuffer_release(info); 197 return ret; 198 } 199 200 dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 201 202 return 0; 203} 204 205static int simplefb_remove(struct platform_device *pdev) 206{ 207 struct fb_info *info = platform_get_drvdata(pdev); 208 209 unregister_framebuffer(info); 210 framebuffer_release(info); 211 212 return 0; 213} 214 215static const struct of_device_id simplefb_of_match[] = { 216 { .compatible = "simple-framebuffer", }, 217 { }, 218}; 219MODULE_DEVICE_TABLE(of, simplefb_of_match); 220 221static struct platform_driver simplefb_driver = { 222 .driver = { 223 .name = "simple-framebuffer", 224 .owner = THIS_MODULE, 225 .of_match_table = simplefb_of_match, 226 }, 227 .probe = simplefb_probe, 228 .remove = simplefb_remove, 229}; 230module_platform_driver(simplefb_driver); 231 232MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); 233MODULE_DESCRIPTION("Simple framebuffer driver"); 234MODULE_LICENSE("GPL v2");