root/drivers/video/fbdev/omap2/omapfb/vrfb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap2_sms_write_rot_control
  2. omap2_sms_write_rot_size
  3. omap2_sms_write_rot_physical_ba
  4. restore_hw_context
  5. get_image_width_roundup
  6. get_extra_physical_size
  7. omap_vrfb_restore_context
  8. omap_vrfb_adjust_size
  9. omap_vrfb_min_phys_size
  10. omap_vrfb_max_height
  11. omap_vrfb_setup
  12. omap_vrfb_map_angle
  13. omap_vrfb_release_ctx
  14. omap_vrfb_request_ctx
  15. omap_vrfb_supported
  16. vrfb_probe
  17. vrfb_remove

   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 */
  46 static unsigned long ctx_map;
  47 
  48 struct vrfb_ctx {
  49         u32 base;
  50         u32 physical_ba;
  51         u32 control;
  52         u32 size;
  53 };
  54 
  55 static 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 
  64 static void __iomem *vrfb_base;
  65 
  66 static int num_ctxs;
  67 static struct vrfb_ctx *ctxs;
  68 
  69 static bool vrfb_loaded;
  70 
  71 static void omap2_sms_write_rot_control(u32 val, unsigned ctx)
  72 {
  73         __raw_writel(val, vrfb_base + SMS_ROT_CONTROL(ctx));
  74 }
  75 
  76 static void omap2_sms_write_rot_size(u32 val, unsigned ctx)
  77 {
  78         __raw_writel(val, vrfb_base + SMS_ROT_SIZE(ctx));
  79 }
  80 
  81 static 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 
  86 static 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 
  93 static 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  */
 107 static 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 
 113 void 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 
 126 void 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 }
 132 EXPORT_SYMBOL(omap_vrfb_adjust_size);
 133 
 134 u32 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 }
 145 EXPORT_SYMBOL(omap_vrfb_min_phys_size);
 146 
 147 u16 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 }
 167 EXPORT_SYMBOL(omap_vrfb_max_height);
 168 
 169 void 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 }
 229 EXPORT_SYMBOL(omap_vrfb_setup);
 230 
 231 int 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 }
 247 EXPORT_SYMBOL(omap_vrfb_map_angle);
 248 
 249 void 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 }
 276 EXPORT_SYMBOL(omap_vrfb_release_ctx);
 277 
 278 int 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;
 324 out:
 325         mutex_unlock(&ctx_lock);
 326         return r;
 327 }
 328 EXPORT_SYMBOL(omap_vrfb_request_ctx);
 329 
 330 bool omap_vrfb_supported(void)
 331 {
 332         return vrfb_loaded;
 333 }
 334 EXPORT_SYMBOL(omap_vrfb_supported);
 335 
 336 static 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 
 373 static void __exit vrfb_remove(struct platform_device *pdev)
 374 {
 375         vrfb_loaded = false;
 376 }
 377 
 378 static struct platform_driver vrfb_driver = {
 379         .driver.name    = "omapvrfb",
 380         .remove         = __exit_p(vrfb_remove),
 381 };
 382 
 383 module_platform_driver_probe(vrfb_driver, vrfb_probe);
 384 
 385 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 386 MODULE_DESCRIPTION("OMAP VRFB");
 387 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */