linux/drivers/video/fbdev/omap2/omapfb/vrfb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * VRFB Rotation Engine
   4 *
   5 * Copyright (C) 2009 Nokia Corporation
   6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
   7 */
   8
   9/*#define DEBUG*/
  10
  11#include <linux/err.h>
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include <linux/ioport.h>
  15#include <linux/io.h>
  16#include <linux/bitops.h>
  17#include <linux/mutex.h>
  18#include <linux/platform_device.h>
  19
  20#include <video/omapvrfb.h>
  21
  22#ifdef DEBUG
  23#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__)
  24#else
  25#define DBG(format, ...)
  26#endif
  27
  28#define SMS_ROT_CONTROL(context)        (0x0 + 0x10 * context)
  29#define SMS_ROT_SIZE(context)           (0x4 + 0x10 * context)
  30#define SMS_ROT_PHYSICAL_BA(context)    (0x8 + 0x10 * context)
  31#define SMS_ROT_VIRT_BASE(rot)          (0x1000000 * (rot))
  32
  33#define OMAP_VRFB_SIZE                  (2048 * 2048 * 4)
  34
  35#define VRFB_PAGE_WIDTH_EXP     5 /* Assuming SDRAM pagesize= 1024 */
  36#define VRFB_PAGE_HEIGHT_EXP    5 /* 1024 = 2^5 * 2^5 */
  37#define VRFB_PAGE_WIDTH         (1 << VRFB_PAGE_WIDTH_EXP)
  38#define VRFB_PAGE_HEIGHT        (1 << VRFB_PAGE_HEIGHT_EXP)
  39#define SMS_IMAGEHEIGHT_OFFSET  16
  40#define SMS_IMAGEWIDTH_OFFSET   0
  41#define SMS_PH_OFFSET           8
  42#define SMS_PW_OFFSET           4
  43#define SMS_PS_OFFSET           0
  44
  45/* bitmap of reserved contexts */
  46static unsigned long ctx_map;
  47
  48struct vrfb_ctx {
  49        u32 base;
  50        u32 physical_ba;
  51        u32 control;
  52        u32 size;
  53};
  54
  55static DEFINE_MUTEX(ctx_lock);
  56
  57/*
  58 * Access to this happens from client drivers or the PM core after wake-up.
  59 * For the first case we require locking at the driver level, for the second
  60 * we don't need locking, since no drivers will run until after the wake-up
  61 * has finished.
  62 */
  63
  64static void __iomem *vrfb_base;
  65
  66static int num_ctxs;
  67static struct vrfb_ctx *ctxs;
  68
  69static bool vrfb_loaded;
  70
  71static void omap2_sms_write_rot_control(u32 val, unsigned ctx)
  72{
  73        __raw_writel(val, vrfb_base + SMS_ROT_CONTROL(ctx));
  74}
  75
  76static void omap2_sms_write_rot_size(u32 val, unsigned ctx)
  77{
  78        __raw_writel(val, vrfb_base + SMS_ROT_SIZE(ctx));
  79}
  80
  81static void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx)
  82{
  83        __raw_writel(val, vrfb_base + SMS_ROT_PHYSICAL_BA(ctx));
  84}
  85
  86static inline void restore_hw_context(int ctx)
  87{
  88        omap2_sms_write_rot_control(ctxs[ctx].control, ctx);
  89        omap2_sms_write_rot_size(ctxs[ctx].size, ctx);
  90        omap2_sms_write_rot_physical_ba(ctxs[ctx].physical_ba, ctx);
  91}
  92
  93static u32 get_image_width_roundup(u16 width, u8 bytespp)
  94{
  95        unsigned long stride = width * bytespp;
  96        unsigned long ceil_pages_per_stride = (stride / VRFB_PAGE_WIDTH) +
  97                (stride % VRFB_PAGE_WIDTH != 0);
  98
  99        return ceil_pages_per_stride * VRFB_PAGE_WIDTH / bytespp;
 100}
 101
 102/*
 103 * This the extra space needed in the VRFB physical area for VRFB to safely wrap
 104 * any memory accesses to the invisible part of the virtual view to the physical
 105 * area.
 106 */
 107static inline u32 get_extra_physical_size(u16 image_width_roundup, u8 bytespp)
 108{
 109        return (OMAP_VRFB_LINE_LEN - image_width_roundup) * VRFB_PAGE_HEIGHT *
 110                bytespp;
 111}
 112
 113void omap_vrfb_restore_context(void)
 114{
 115        int i;
 116        unsigned long map = ctx_map;
 117
 118        for (i = ffs(map); i; i = ffs(map)) {
 119                /* i=1..32 */
 120                i--;
 121                map &= ~(1 << i);
 122                restore_hw_context(i);
 123        }
 124}
 125
 126void omap_vrfb_adjust_size(u16 *width, u16 *height,
 127                u8 bytespp)
 128{
 129        *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
 130        *height = ALIGN(*height, VRFB_PAGE_HEIGHT);
 131}
 132EXPORT_SYMBOL(omap_vrfb_adjust_size);
 133
 134u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp)
 135{
 136        unsigned long image_width_roundup = get_image_width_roundup(width,
 137                bytespp);
 138
 139        if (image_width_roundup > OMAP_VRFB_LINE_LEN)
 140                return 0;
 141
 142        return (width * height * bytespp) + get_extra_physical_size(
 143                image_width_roundup, bytespp);
 144}
 145EXPORT_SYMBOL(omap_vrfb_min_phys_size);
 146
 147u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp)
 148{
 149        unsigned long image_width_roundup = get_image_width_roundup(width,
 150                bytespp);
 151        unsigned long height;
 152        unsigned long extra;
 153
 154        if (image_width_roundup > OMAP_VRFB_LINE_LEN)
 155                return 0;
 156
 157        extra = get_extra_physical_size(image_width_roundup, bytespp);
 158
 159        if (phys_size < extra)
 160                return 0;
 161
 162        height = (phys_size - extra) / (width * bytespp);
 163
 164        /* Virtual views provided by VRFB are limited to 2048x2048. */
 165        return min_t(unsigned long, height, 2048);
 166}
 167EXPORT_SYMBOL(omap_vrfb_max_height);
 168
 169void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
 170                u16 width, u16 height,
 171                unsigned bytespp, bool yuv_mode)
 172{
 173        unsigned pixel_size_exp;
 174        u16 vrfb_width;
 175        u16 vrfb_height;
 176        u8 ctx = vrfb->context;
 177        u32 size;
 178        u32 control;
 179
 180        DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d, %d)\n", ctx, paddr,
 181                        width, height, bytespp, yuv_mode);
 182
 183        /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit
 184         * differently. See TRM. */
 185        if (yuv_mode) {
 186                bytespp *= 2;
 187                width /= 2;
 188        }
 189
 190        if (bytespp == 4)
 191                pixel_size_exp = 2;
 192        else if (bytespp == 2)
 193                pixel_size_exp = 1;
 194        else {
 195                BUG();
 196                return;
 197        }
 198
 199        vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
 200        vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT);
 201
 202        DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp);
 203
 204        size  = vrfb_width << SMS_IMAGEWIDTH_OFFSET;
 205        size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET;
 206
 207        control  = pixel_size_exp << SMS_PS_OFFSET;
 208        control |= VRFB_PAGE_WIDTH_EXP  << SMS_PW_OFFSET;
 209        control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET;
 210
 211        ctxs[ctx].physical_ba = paddr;
 212        ctxs[ctx].size = size;
 213        ctxs[ctx].control = control;
 214
 215        omap2_sms_write_rot_physical_ba(paddr, ctx);
 216        omap2_sms_write_rot_size(size, ctx);
 217        omap2_sms_write_rot_control(control, ctx);
 218
 219        DBG("vrfb offset pixels %d, %d\n",
 220                        vrfb_width - width, vrfb_height - height);
 221
 222        vrfb->xres = width;
 223        vrfb->yres = height;
 224        vrfb->xoffset = vrfb_width - width;
 225        vrfb->yoffset = vrfb_height - height;
 226        vrfb->bytespp = bytespp;
 227        vrfb->yuv_mode = yuv_mode;
 228}
 229EXPORT_SYMBOL(omap_vrfb_setup);
 230
 231int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot)
 232{
 233        unsigned long size = height * OMAP_VRFB_LINE_LEN * vrfb->bytespp;
 234
 235        vrfb->vaddr[rot] = ioremap_wc(vrfb->paddr[rot], size);
 236
 237        if (!vrfb->vaddr[rot]) {
 238                printk(KERN_ERR "vrfb: ioremap failed\n");
 239                return -ENOMEM;
 240        }
 241
 242        DBG("ioremapped vrfb area %d of size %lu into %p\n", rot, size,
 243                vrfb->vaddr[rot]);
 244
 245        return 0;
 246}
 247EXPORT_SYMBOL(omap_vrfb_map_angle);
 248
 249void omap_vrfb_release_ctx(struct vrfb *vrfb)
 250{
 251        int rot;
 252        int ctx = vrfb->context;
 253
 254        if (ctx == 0xff)
 255                return;
 256
 257        DBG("release ctx %d\n", ctx);
 258
 259        mutex_lock(&ctx_lock);
 260
 261        BUG_ON(!(ctx_map & (1 << ctx)));
 262
 263        clear_bit(ctx, &ctx_map);
 264
 265        for (rot = 0; rot < 4; ++rot) {
 266                if (vrfb->paddr[rot]) {
 267                        release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE);
 268                        vrfb->paddr[rot] = 0;
 269                }
 270        }
 271
 272        vrfb->context = 0xff;
 273
 274        mutex_unlock(&ctx_lock);
 275}
 276EXPORT_SYMBOL(omap_vrfb_release_ctx);
 277
 278int omap_vrfb_request_ctx(struct vrfb *vrfb)
 279{
 280        int rot;
 281        u32 paddr;
 282        u8 ctx;
 283        int r;
 284
 285        DBG("request ctx\n");
 286
 287        mutex_lock(&ctx_lock);
 288
 289        for (ctx = 0; ctx < num_ctxs; ++ctx)
 290                if ((ctx_map & (1 << ctx)) == 0)
 291                        break;
 292
 293        if (ctx == num_ctxs) {
 294                pr_err("vrfb: no free contexts\n");
 295                r = -EBUSY;
 296                goto out;
 297        }
 298
 299        DBG("found free ctx %d\n", ctx);
 300
 301        set_bit(ctx, &ctx_map);
 302
 303        memset(vrfb, 0, sizeof(*vrfb));
 304
 305        vrfb->context = ctx;
 306
 307        for (rot = 0; rot < 4; ++rot) {
 308                paddr = ctxs[ctx].base + SMS_ROT_VIRT_BASE(rot);
 309                if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) {
 310                        pr_err("vrfb: failed to reserve VRFB "
 311                                        "area for ctx %d, rotation %d\n",
 312                                        ctx, rot * 90);
 313                        omap_vrfb_release_ctx(vrfb);
 314                        r = -ENOMEM;
 315                        goto out;
 316                }
 317
 318                vrfb->paddr[rot] = paddr;
 319
 320                DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]);
 321        }
 322
 323        r = 0;
 324out:
 325        mutex_unlock(&ctx_lock);
 326        return r;
 327}
 328EXPORT_SYMBOL(omap_vrfb_request_ctx);
 329
 330bool omap_vrfb_supported(void)
 331{
 332        return vrfb_loaded;
 333}
 334EXPORT_SYMBOL(omap_vrfb_supported);
 335
 336static int __init vrfb_probe(struct platform_device *pdev)
 337{
 338        struct resource *mem;
 339        int i;
 340
 341        /* first resource is the register res, the rest are vrfb contexts */
 342
 343        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 344        vrfb_base = devm_ioremap_resource(&pdev->dev, mem);
 345        if (IS_ERR(vrfb_base))
 346                return PTR_ERR(vrfb_base);
 347
 348        num_ctxs = pdev->num_resources - 1;
 349
 350        ctxs = devm_kcalloc(&pdev->dev,
 351                        num_ctxs, sizeof(struct vrfb_ctx),
 352                        GFP_KERNEL);
 353
 354        if (!ctxs)
 355                return -ENOMEM;
 356
 357        for (i = 0; i < num_ctxs; ++i) {
 358                mem = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
 359                if (!mem) {
 360                        dev_err(&pdev->dev, "can't get vrfb ctx %d address\n",
 361                                        i);
 362                        return -EINVAL;
 363                }
 364
 365                ctxs[i].base = mem->start;
 366        }
 367
 368        vrfb_loaded = true;
 369
 370        return 0;
 371}
 372
 373static void __exit vrfb_remove(struct platform_device *pdev)
 374{
 375        vrfb_loaded = false;
 376}
 377
 378static struct platform_driver vrfb_driver = {
 379        .driver.name    = "omapvrfb",
 380        .remove         = __exit_p(vrfb_remove),
 381};
 382
 383module_platform_driver_probe(vrfb_driver, vrfb_probe);
 384
 385MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 386MODULE_DESCRIPTION("OMAP VRFB");
 387MODULE_LICENSE("GPL v2");
 388