]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/video/omap2/omapfb/omapfb-ioctl.c
Merge branch 'hwpoison-cleanups' into hwpoison
[mv-sheeva.git] / drivers / video / omap2 / omapfb / omapfb-ioctl.c
index 9c7361871d786b4685e7469fa0eb99b5fa16c4b0..6f435450987e7596d1e81963ea53bcc82bcd3f42 100644 (file)
 
 #include "omapfb.h"
 
+static u8 get_mem_idx(struct omapfb_info *ofbi)
+{
+       if (ofbi->id == ofbi->region->id)
+               return 0;
+
+       return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id;
+}
+
+static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi,
+                                                u8 mem_idx)
+{
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+
+       if (mem_idx & OMAPFB_MEM_IDX_ENABLED)
+               mem_idx &= OMAPFB_MEM_IDX_MASK;
+       else
+               mem_idx = ofbi->id;
+
+       if (mem_idx >= fbdev->num_fbs)
+               return NULL;
+
+       return &fbdev->regions[mem_idx];
+}
+
 static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
 {
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omap_overlay *ovl;
-       struct omap_overlay_info info;
+       struct omap_overlay_info old_info;
+       struct omapfb2_mem_region *old_rg, *new_rg;
        int r = 0;
 
        DBG("omapfb_setup_plane\n");
@@ -52,36 +77,106 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
        /* XXX uses only the first overlay */
        ovl = ofbi->overlays[0];
 
-       if (pi->enabled && !ofbi->region.size) {
+       old_rg = ofbi->region;
+       new_rg = get_mem_region(ofbi, pi->mem_idx);
+       if (!new_rg) {
+               r = -EINVAL;
+               goto out;
+       }
+
+       /* Take the locks in a specific order to keep lockdep happy */
+       if (old_rg->id < new_rg->id) {
+               omapfb_get_mem_region(old_rg);
+               omapfb_get_mem_region(new_rg);
+       } else if (new_rg->id < old_rg->id) {
+               omapfb_get_mem_region(new_rg);
+               omapfb_get_mem_region(old_rg);
+       } else
+               omapfb_get_mem_region(old_rg);
+
+       if (pi->enabled && !new_rg->size) {
                /*
                 * This plane's memory was freed, can't enable it
                 * until it's reallocated.
                 */
                r = -EINVAL;
-               goto out;
+               goto put_mem;
        }
 
-       ovl->get_overlay_info(ovl, &info);
+       ovl->get_overlay_info(ovl, &old_info);
 
-       info.pos_x = pi->pos_x;
-       info.pos_y = pi->pos_y;
-       info.out_width = pi->out_width;
-       info.out_height = pi->out_height;
-       info.enabled = pi->enabled;
+       if (old_rg != new_rg) {
+               ofbi->region = new_rg;
+               set_fb_fix(fbi);
+       }
 
-       r = ovl->set_overlay_info(ovl, &info);
-       if (r)
-               goto out;
+       if (pi->enabled) {
+               struct omap_overlay_info info;
 
-       if (ovl->manager) {
-               r = ovl->manager->apply(ovl->manager);
+               r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
+                       pi->out_width, pi->out_height);
                if (r)
-                       goto out;
+                       goto undo;
+
+               ovl->get_overlay_info(ovl, &info);
+
+               if (!info.enabled) {
+                       info.enabled = pi->enabled;
+                       r = ovl->set_overlay_info(ovl, &info);
+                       if (r)
+                               goto undo;
+               }
+       } else {
+               struct omap_overlay_info info;
+
+               ovl->get_overlay_info(ovl, &info);
+
+               info.enabled = pi->enabled;
+               info.pos_x = pi->pos_x;
+               info.pos_y = pi->pos_y;
+               info.out_width = pi->out_width;
+               info.out_height = pi->out_height;
+
+               r = ovl->set_overlay_info(ovl, &info);
+               if (r)
+                       goto undo;
        }
 
-out:
-       if (r)
-               dev_err(fbdev->dev, "setup_plane failed\n");
+       if (ovl->manager)
+               ovl->manager->apply(ovl->manager);
+
+       /* Release the locks in a specific order to keep lockdep happy */
+       if (old_rg->id > new_rg->id) {
+               omapfb_put_mem_region(old_rg);
+               omapfb_put_mem_region(new_rg);
+       } else if (new_rg->id > old_rg->id) {
+               omapfb_put_mem_region(new_rg);
+               omapfb_put_mem_region(old_rg);
+       } else
+               omapfb_put_mem_region(old_rg);
+
+       return 0;
+
+ undo:
+       if (old_rg != new_rg) {
+               ofbi->region = old_rg;
+               set_fb_fix(fbi);
+       }
+
+       ovl->set_overlay_info(ovl, &old_info);
+ put_mem:
+       /* Release the locks in a specific order to keep lockdep happy */
+       if (old_rg->id > new_rg->id) {
+               omapfb_put_mem_region(old_rg);
+               omapfb_put_mem_region(new_rg);
+       } else if (new_rg->id > old_rg->id) {
+               omapfb_put_mem_region(new_rg);
+               omapfb_put_mem_region(old_rg);
+       } else
+               omapfb_put_mem_region(old_rg);
+ out:
+       dev_err(fbdev->dev, "setup_plane failed\n");
+
        return r;
 }
 
@@ -92,8 +187,8 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
        if (ofbi->num_overlays != 1) {
                memset(pi, 0, sizeof(*pi));
        } else {
-               struct omap_overlay_info *ovli;
                struct omap_overlay *ovl;
+               struct omap_overlay_info *ovli;
 
                ovl = ofbi->overlays[0];
                ovli = &ovl->info;
@@ -103,6 +198,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
                pi->enabled = ovli->enabled;
                pi->channel_out = 0; /* xxx */
                pi->mirror = 0;
+               pi->mem_idx = get_mem_idx(ofbi);
                pi->out_width = ovli->out_width;
                pi->out_height = ovli->out_height;
        }
@@ -115,7 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_device *fbdev = ofbi->fbdev;
        struct omapfb2_mem_region *rg;
-       int r, i;
+       int r = 0, i;
        size_t size;
 
        if (mi->type > OMAPFB_MEMTYPE_MAX)
@@ -123,22 +219,44 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
 
        size = PAGE_ALIGN(mi->size);
 
-       rg = &ofbi->region;
+       rg = ofbi->region;
 
-       for (i = 0; i < ofbi->num_overlays; i++) {
-               if (ofbi->overlays[i]->info.enabled)
-                       return -EBUSY;
+       down_write_nested(&rg->lock, rg->id);
+       atomic_inc(&rg->lock_count);
+
+       if (atomic_read(&rg->map_count)) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
+               int j;
+
+               if (ofbi2->region != rg)
+                       continue;
+
+               for (j = 0; j < ofbi2->num_overlays; j++) {
+                       if (ofbi2->overlays[j]->info.enabled) {
+                               r = -EBUSY;
+                               goto out;
+                       }
+               }
        }
 
        if (rg->size != size || rg->type != mi->type) {
                r = omapfb_realloc_fbmem(fbi, size, mi->type);
                if (r) {
                        dev_err(fbdev->dev, "realloc fbmem failed\n");
-                       return r;
+                       goto out;
                }
        }
 
-       return 0;
+ out:
+       atomic_dec(&rg->lock_count);
+       up_write(&rg->lock);
+
+       return r;
 }
 
 static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
@@ -146,12 +264,14 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
        struct omapfb_info *ofbi = FB2OFB(fbi);
        struct omapfb2_mem_region *rg;
 
-       rg = &ofbi->region;
+       rg = omapfb_get_mem_region(ofbi->region);
        memset(mi, 0, sizeof(*mi));
 
        mi->size = rg->size;
        mi->type = rg->type;
 
+       omapfb_put_mem_region(rg);
+
        return 0;
 }
 
@@ -490,6 +610,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                struct omapfb_vram_info         vram_info;
                struct omapfb_tearsync_info     tearsync_info;
                struct omapfb_display_info      display_info;
+               u32                             crt;
        } p;
 
        int r = 0;
@@ -648,6 +769,17 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                        r = -EFAULT;
                break;
 
+       case FBIO_WAITFORVSYNC:
+               if (get_user(p.crt, (__u32 __user *)arg)) {
+                       r = -EFAULT;
+                       break;
+               }
+               if (p.crt != 0) {
+                       r = -ENODEV;
+                       break;
+               }
+               /* FALLTHROUGH */
+
        case OMAPFB_WAITFORVSYNC:
                DBG("ioctl WAITFORVSYNC\n");
                if (!display) {
@@ -738,7 +870,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
                        break;
                }
 
-               if (!display->driver->enable_te) {
+               if (!display || !display->driver->enable_te) {
                        r = -ENODEV;
                        break;
                }