OMAPDSS: APPLY: add checking of ovls/mgrs settings
authorTomi Valkeinen <tomi.valkeinen@ti.com>
Thu, 17 Nov 2011 15:35:28 +0000 (17:35 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Fri, 2 Dec 2011 06:54:53 +0000 (08:54 +0200)
Add checks for overlay and manager settings. The checks are a bit
complex, as we need to observe the bigger picture instead of overlays
and managers independently. Things like the used display and the zorder
of other overlays affect the validity of the settings.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
drivers/video/omap2/dss/apply.c

index 242cb1c983c062fdd34661733f87ae02904cb5ca..6eb48586501cdc5601b70b121c269a958b8864d3 100644 (file)
@@ -166,6 +166,169 @@ static bool mgr_manual_update(struct omap_overlay_manager *mgr)
        return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
 }
 
+/* Check if overlay parameters are compatible with display */
+static int dss_ovl_check(struct omap_overlay *ovl,
+               struct omap_overlay_info *info, struct omap_dss_device *dssdev)
+{
+       u16 outw, outh;
+       u16 dw, dh;
+
+       if (dssdev == NULL)
+               return 0;
+
+       dssdev->driver->get_resolution(dssdev, &dw, &dh);
+
+       if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+               outw = info->width;
+               outh = info->height;
+       } else {
+               if (info->out_width == 0)
+                       outw = info->width;
+               else
+                       outw = info->out_width;
+
+               if (info->out_height == 0)
+                       outh = info->height;
+               else
+                       outh = info->out_height;
+       }
+
+       if (dw < info->pos_x + outw) {
+               DSSERR("overlay %d horizontally not inside the display area "
+                               "(%d + %d >= %d)\n",
+                               ovl->id, info->pos_x, outw, dw);
+               return -EINVAL;
+       }
+
+       if (dh < info->pos_y + outh) {
+               DSSERR("overlay %d vertically not inside the display area "
+                               "(%d + %d >= %d)\n",
+                               ovl->id, info->pos_y, outh, dh);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
+               struct omap_overlay_info **overlay_infos)
+{
+       struct omap_overlay *ovl1, *ovl2;
+       struct ovl_priv_data *op1, *op2;
+       struct omap_overlay_info *info1, *info2;
+
+       list_for_each_entry(ovl1, &mgr->overlays, list) {
+               op1 = get_ovl_priv(ovl1);
+               info1 = overlay_infos[ovl1->id];
+
+               if (info1 == NULL)
+                       continue;
+
+               list_for_each_entry(ovl2, &mgr->overlays, list) {
+                       if (ovl1 == ovl2)
+                               continue;
+
+                       op2 = get_ovl_priv(ovl2);
+                       info2 = overlay_infos[ovl2->id];
+
+                       if (info2 == NULL)
+                               continue;
+
+                       if (info1->zorder == info2->zorder) {
+                               DSSERR("overlays %d and %d have the same "
+                                               "zorder %d\n",
+                                       ovl1->id, ovl2->id, info1->zorder);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int dss_mgr_check(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev,
+               struct omap_overlay_manager_info *info,
+               struct omap_overlay_info **overlay_infos)
+{
+       struct omap_overlay *ovl;
+       int r;
+
+       if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
+               r = dss_mgr_check_zorder(mgr, overlay_infos);
+               if (r)
+                       return r;
+       }
+
+       list_for_each_entry(ovl, &mgr->overlays, list) {
+               struct omap_overlay_info *oi;
+               int r;
+
+               oi = overlay_infos[ovl->id];
+
+               if (oi == NULL)
+                       continue;
+
+               r = dss_ovl_check(ovl, oi, dssdev);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+static int dss_check_settings_low(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev, bool applying)
+{
+       struct omap_overlay_info *oi;
+       struct omap_overlay_manager_info *mi;
+       struct omap_overlay *ovl;
+       struct omap_overlay_info *ois[MAX_DSS_OVERLAYS];
+       struct ovl_priv_data *op;
+       struct mgr_priv_data *mp;
+
+       mp = get_mgr_priv(mgr);
+
+       if (applying && mp->user_info_dirty)
+               mi = &mp->user_info;
+       else
+               mi = &mp->info;
+
+       /* collect the infos to be tested into the array */
+       list_for_each_entry(ovl, &mgr->overlays, list) {
+               op = get_ovl_priv(ovl);
+
+               if (!op->enabled)
+                       oi = NULL;
+               else if (applying && op->user_info_dirty)
+                       oi = &op->user_info;
+               else
+                       oi = &op->info;
+
+               ois[ovl->id] = oi;
+       }
+
+       return dss_mgr_check(mgr, dssdev, mi, ois);
+}
+
+/*
+ * check manager and overlay settings using overlay_info from data->info
+ */
+static int dss_check_settings(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev)
+{
+       return dss_check_settings_low(mgr, dssdev, false);
+}
+
+/*
+ * check manager and overlay settings using overlay_info from ovl->info if
+ * dirty and from data->info otherwise
+ */
+static int dss_check_settings_apply(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev)
+{
+       return dss_check_settings_low(mgr, dssdev, true);
+}
+
 static bool need_isr(void)
 {
        const int num_mgrs = dss_feat_get_num_mgrs();
@@ -517,6 +680,7 @@ static void dss_write_regs(void)
        for (i = 0; i < num_mgrs; ++i) {
                struct omap_overlay_manager *mgr;
                struct mgr_priv_data *mp;
+               int r;
 
                mgr = omap_dss_get_overlay_manager(i);
                mp = get_mgr_priv(mgr);
@@ -524,6 +688,13 @@ static void dss_write_regs(void)
                if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
                        continue;
 
+               r = dss_check_settings(mgr, mgr->device);
+               if (r) {
+                       DSSERR("cannot write registers for manager %s: "
+                                       "illegal configuration\n", mgr->name);
+                       continue;
+               }
+
                dss_mgr_write_regs(mgr);
 
                if (need_go(mgr)) {
@@ -541,11 +712,19 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
 {
        struct mgr_priv_data *mp = get_mgr_priv(mgr);
        unsigned long flags;
+       int r;
 
        spin_lock_irqsave(&data_lock, flags);
 
        WARN_ON(mp->updating);
 
+       r = dss_check_settings(mgr, mgr->device);
+       if (r) {
+               DSSERR("cannot start manual update: illegal configuration\n");
+               spin_unlock_irqrestore(&data_lock, flags);
+               return;
+       }
+
        dss_mgr_write_regs(mgr);
 
        mp->updating = true;
@@ -690,11 +869,19 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
 {
        unsigned long flags;
        struct omap_overlay *ovl;
+       int r;
 
        DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
 
        spin_lock_irqsave(&data_lock, flags);
 
+       r = dss_check_settings_apply(mgr, mgr->device);
+       if (r) {
+               spin_unlock_irqrestore(&data_lock, flags);
+               DSSERR("failed to apply settings: illegal configuration.\n");
+               return r;
+       }
+
        /* Configure overlays */
        list_for_each_entry(ovl, &mgr->overlays, list)
                omap_dss_mgr_apply_ovl(ovl);
@@ -784,6 +971,7 @@ void dss_mgr_enable(struct omap_overlay_manager *mgr)
 {
        struct mgr_priv_data *mp = get_mgr_priv(mgr);
        unsigned long flags;
+       int r;
 
        mutex_lock(&apply_lock);
 
@@ -792,6 +980,16 @@ void dss_mgr_enable(struct omap_overlay_manager *mgr)
 
        spin_lock_irqsave(&data_lock, flags);
 
+       mp->enabled = true;
+       r = dss_check_settings(mgr, mgr->device);
+       mp->enabled = false;
+       if (r) {
+               DSSERR("failed to enable manager %d: check_settings failed\n",
+                               mgr->id);
+               spin_unlock_irqrestore(&data_lock, flags);
+               goto out;
+       }
+
        mp->enabled = true;
 
        dss_mgr_setup_fifos(mgr);
@@ -1142,16 +1340,25 @@ int dss_ovl_enable(struct omap_overlay *ovl)
 
        if (op->enabled) {
                r = 0;
-               goto err;
+               goto err1;
        }
 
        if (ovl->manager == NULL || ovl->manager->device == NULL) {
                r = -EINVAL;
-               goto err;
+               goto err1;
        }
 
        spin_lock_irqsave(&data_lock, flags);
 
+       op->enabled = true;
+       r = dss_check_settings(ovl->manager, ovl->manager->device);
+       op->enabled = false;
+       if (r) {
+               DSSERR("failed to enable overlay %d: check_settings failed\n",
+                               ovl->id);
+               goto err2;
+       }
+
        dss_apply_ovl_enable(ovl, true);
 
        dss_ovl_setup_fifo(ovl);
@@ -1163,7 +1370,9 @@ int dss_ovl_enable(struct omap_overlay *ovl)
        mutex_unlock(&apply_lock);
 
        return 0;
-err:
+err2:
+       spin_unlock_irqrestore(&data_lock, flags);
+err1:
        mutex_unlock(&apply_lock);
        return r;
 }