summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2011-11-17 16:35:28 +0100
committerTomi Valkeinen <tomi.valkeinen@ti.com>2011-12-02 07:54:53 +0100
commit39518356ccd6e439abae24e1a24d84dcd12ff207 (patch)
treee7579f97b57961cf6b07a48fa2728cb9758c7277 /drivers/video
parentOMAPDSS: APPLY: add dss_mgr_simple_check() (diff)
downloadlinux-39518356ccd6e439abae24e1a24d84dcd12ff207.tar.xz
linux-39518356ccd6e439abae24e1a24d84dcd12ff207.zip
OMAPDSS: APPLY: add checking of ovls/mgrs settings
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>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/omap2/dss/apply.c215
1 files changed, 212 insertions, 3 deletions
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 242cb1c983c0..6eb48586501c 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -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);
@@ -793,6 +981,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;
}