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