linux/drivers/video/simplefb.c
<<
>>
Prefs
   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");
 265