| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/drivers/video/omap2/dss/manager.c | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2009 Nokia Corporation | 
|  | 5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | 
|  | 6 | * | 
|  | 7 | * Some code and ideas taken from drivers/video/omap/ driver | 
|  | 8 | * by Imre Deak. | 
|  | 9 | * | 
|  | 10 | * This program is free software; you can redistribute it and/or modify it | 
|  | 11 | * under the terms of the GNU General Public License version 2 as published by | 
|  | 12 | * the Free Software Foundation. | 
|  | 13 | * | 
|  | 14 | * This program is distributed in the hope that it will be useful, but WITHOUT | 
|  | 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | 16 | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | 17 | * more details. | 
|  | 18 | * | 
|  | 19 | * You should have received a copy of the GNU General Public License along with | 
|  | 20 | * this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 21 | */ | 
|  | 22 |  | 
|  | 23 | #define DSS_SUBSYS_NAME "MANAGER" | 
|  | 24 |  | 
|  | 25 | #include <linux/kernel.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 26 | #include <linux/slab.h> | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 27 | #include <linux/module.h> | 
|  | 28 | #include <linux/platform_device.h> | 
|  | 29 | #include <linux/spinlock.h> | 
|  | 30 | #include <linux/jiffies.h> | 
|  | 31 |  | 
| Tomi Valkeinen | a0b38cc | 2011-05-11 14:05:07 +0300 | [diff] [blame] | 32 | #include <video/omapdss.h> | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 33 | #include <plat/cpu.h> | 
|  | 34 |  | 
|  | 35 | #include "dss.h" | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 36 | #include "dss_features.h" | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 37 |  | 
|  | 38 | static int num_managers; | 
|  | 39 | static struct list_head manager_list; | 
|  | 40 |  | 
|  | 41 | static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) | 
|  | 42 | { | 
|  | 43 | return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) | 
|  | 47 | { | 
|  | 48 | return snprintf(buf, PAGE_SIZE, "%s\n", | 
|  | 49 | mgr->device ? mgr->device->name : "<none>"); | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | static ssize_t manager_display_store(struct omap_overlay_manager *mgr, | 
|  | 53 | const char *buf, size_t size) | 
|  | 54 | { | 
|  | 55 | int r = 0; | 
|  | 56 | size_t len = size; | 
|  | 57 | struct omap_dss_device *dssdev = NULL; | 
|  | 58 |  | 
|  | 59 | int match(struct omap_dss_device *dssdev, void *data) | 
|  | 60 | { | 
|  | 61 | const char *str = data; | 
|  | 62 | return sysfs_streq(dssdev->name, str); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | if (buf[size-1] == '\n') | 
|  | 66 | --len; | 
|  | 67 |  | 
|  | 68 | if (len > 0) | 
|  | 69 | dssdev = omap_dss_find_device((void *)buf, match); | 
|  | 70 |  | 
|  | 71 | if (len > 0 && dssdev == NULL) | 
|  | 72 | return -EINVAL; | 
|  | 73 |  | 
|  | 74 | if (dssdev) | 
|  | 75 | DSSDBG("display %s found\n", dssdev->name); | 
|  | 76 |  | 
|  | 77 | if (mgr->device) { | 
|  | 78 | r = mgr->unset_device(mgr); | 
|  | 79 | if (r) { | 
|  | 80 | DSSERR("failed to unset display\n"); | 
|  | 81 | goto put_device; | 
|  | 82 | } | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | if (dssdev) { | 
|  | 86 | r = mgr->set_device(mgr, dssdev); | 
|  | 87 | if (r) { | 
|  | 88 | DSSERR("failed to set manager\n"); | 
|  | 89 | goto put_device; | 
|  | 90 | } | 
|  | 91 |  | 
|  | 92 | r = mgr->apply(mgr); | 
|  | 93 | if (r) { | 
|  | 94 | DSSERR("failed to apply dispc config\n"); | 
|  | 95 | goto put_device; | 
|  | 96 | } | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | put_device: | 
|  | 100 | if (dssdev) | 
|  | 101 | omap_dss_put_device(dssdev); | 
|  | 102 |  | 
|  | 103 | return r ? r : size; | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, | 
|  | 107 | char *buf) | 
|  | 108 | { | 
|  | 109 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, | 
|  | 113 | const char *buf, size_t size) | 
|  | 114 | { | 
|  | 115 | struct omap_overlay_manager_info info; | 
|  | 116 | u32 color; | 
|  | 117 | int r; | 
|  | 118 |  | 
|  | 119 | if (sscanf(buf, "%d", &color) != 1) | 
|  | 120 | return -EINVAL; | 
|  | 121 |  | 
|  | 122 | mgr->get_manager_info(mgr, &info); | 
|  | 123 |  | 
|  | 124 | info.default_color = color; | 
|  | 125 |  | 
|  | 126 | r = mgr->set_manager_info(mgr, &info); | 
|  | 127 | if (r) | 
|  | 128 | return r; | 
|  | 129 |  | 
|  | 130 | r = mgr->apply(mgr); | 
|  | 131 | if (r) | 
|  | 132 | return r; | 
|  | 133 |  | 
|  | 134 | return size; | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | static const char *trans_key_type_str[] = { | 
|  | 138 | "gfx-destination", | 
|  | 139 | "video-source", | 
|  | 140 | }; | 
|  | 141 |  | 
|  | 142 | static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, | 
|  | 143 | char *buf) | 
|  | 144 | { | 
|  | 145 | enum omap_dss_trans_key_type key_type; | 
|  | 146 |  | 
|  | 147 | key_type = mgr->info.trans_key_type; | 
|  | 148 | BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); | 
|  | 149 |  | 
|  | 150 | return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, | 
|  | 154 | const char *buf, size_t size) | 
|  | 155 | { | 
|  | 156 | enum omap_dss_trans_key_type key_type; | 
|  | 157 | struct omap_overlay_manager_info info; | 
|  | 158 | int r; | 
|  | 159 |  | 
|  | 160 | for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | 
|  | 161 | key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { | 
|  | 162 | if (sysfs_streq(buf, trans_key_type_str[key_type])) | 
|  | 163 | break; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | if (key_type == ARRAY_SIZE(trans_key_type_str)) | 
|  | 167 | return -EINVAL; | 
|  | 168 |  | 
|  | 169 | mgr->get_manager_info(mgr, &info); | 
|  | 170 |  | 
|  | 171 | info.trans_key_type = key_type; | 
|  | 172 |  | 
|  | 173 | r = mgr->set_manager_info(mgr, &info); | 
|  | 174 | if (r) | 
|  | 175 | return r; | 
|  | 176 |  | 
|  | 177 | r = mgr->apply(mgr); | 
|  | 178 | if (r) | 
|  | 179 | return r; | 
|  | 180 |  | 
|  | 181 | return size; | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, | 
|  | 185 | char *buf) | 
|  | 186 | { | 
|  | 187 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, | 
|  | 191 | const char *buf, size_t size) | 
|  | 192 | { | 
|  | 193 | struct omap_overlay_manager_info info; | 
|  | 194 | u32 key_value; | 
|  | 195 | int r; | 
|  | 196 |  | 
|  | 197 | if (sscanf(buf, "%d", &key_value) != 1) | 
|  | 198 | return -EINVAL; | 
|  | 199 |  | 
|  | 200 | mgr->get_manager_info(mgr, &info); | 
|  | 201 |  | 
|  | 202 | info.trans_key = key_value; | 
|  | 203 |  | 
|  | 204 | r = mgr->set_manager_info(mgr, &info); | 
|  | 205 | if (r) | 
|  | 206 | return r; | 
|  | 207 |  | 
|  | 208 | r = mgr->apply(mgr); | 
|  | 209 | if (r) | 
|  | 210 | return r; | 
|  | 211 |  | 
|  | 212 | return size; | 
|  | 213 | } | 
|  | 214 |  | 
|  | 215 | static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, | 
|  | 216 | char *buf) | 
|  | 217 | { | 
|  | 218 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, | 
|  | 222 | const char *buf, size_t size) | 
|  | 223 | { | 
|  | 224 | struct omap_overlay_manager_info info; | 
|  | 225 | int enable; | 
|  | 226 | int r; | 
|  | 227 |  | 
|  | 228 | if (sscanf(buf, "%d", &enable) != 1) | 
|  | 229 | return -EINVAL; | 
|  | 230 |  | 
|  | 231 | mgr->get_manager_info(mgr, &info); | 
|  | 232 |  | 
|  | 233 | info.trans_enabled = enable ? true : false; | 
|  | 234 |  | 
|  | 235 | r = mgr->set_manager_info(mgr, &info); | 
|  | 236 | if (r) | 
|  | 237 | return r; | 
|  | 238 |  | 
|  | 239 | r = mgr->apply(mgr); | 
|  | 240 | if (r) | 
|  | 241 | return r; | 
|  | 242 |  | 
|  | 243 | return size; | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | static ssize_t manager_alpha_blending_enabled_show( | 
|  | 247 | struct omap_overlay_manager *mgr, char *buf) | 
|  | 248 | { | 
|  | 249 | return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); | 
|  | 250 | } | 
|  | 251 |  | 
|  | 252 | static ssize_t manager_alpha_blending_enabled_store( | 
|  | 253 | struct omap_overlay_manager *mgr, | 
|  | 254 | const char *buf, size_t size) | 
|  | 255 | { | 
|  | 256 | struct omap_overlay_manager_info info; | 
|  | 257 | int enable; | 
|  | 258 | int r; | 
|  | 259 |  | 
|  | 260 | if (sscanf(buf, "%d", &enable) != 1) | 
|  | 261 | return -EINVAL; | 
|  | 262 |  | 
|  | 263 | mgr->get_manager_info(mgr, &info); | 
|  | 264 |  | 
|  | 265 | info.alpha_enabled = enable ? true : false; | 
|  | 266 |  | 
|  | 267 | r = mgr->set_manager_info(mgr, &info); | 
|  | 268 | if (r) | 
|  | 269 | return r; | 
|  | 270 |  | 
|  | 271 | r = mgr->apply(mgr); | 
|  | 272 | if (r) | 
|  | 273 | return r; | 
|  | 274 |  | 
|  | 275 | return size; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | struct manager_attribute { | 
|  | 279 | struct attribute attr; | 
|  | 280 | ssize_t (*show)(struct omap_overlay_manager *, char *); | 
|  | 281 | ssize_t	(*store)(struct omap_overlay_manager *, const char *, size_t); | 
|  | 282 | }; | 
|  | 283 |  | 
|  | 284 | #define MANAGER_ATTR(_name, _mode, _show, _store) \ | 
|  | 285 | struct manager_attribute manager_attr_##_name = \ | 
|  | 286 | __ATTR(_name, _mode, _show, _store) | 
|  | 287 |  | 
|  | 288 | static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); | 
|  | 289 | static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, | 
|  | 290 | manager_display_show, manager_display_store); | 
|  | 291 | static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, | 
|  | 292 | manager_default_color_show, manager_default_color_store); | 
|  | 293 | static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, | 
|  | 294 | manager_trans_key_type_show, manager_trans_key_type_store); | 
|  | 295 | static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, | 
|  | 296 | manager_trans_key_value_show, manager_trans_key_value_store); | 
|  | 297 | static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, | 
|  | 298 | manager_trans_key_enabled_show, | 
|  | 299 | manager_trans_key_enabled_store); | 
|  | 300 | static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, | 
|  | 301 | manager_alpha_blending_enabled_show, | 
|  | 302 | manager_alpha_blending_enabled_store); | 
|  | 303 |  | 
|  | 304 |  | 
|  | 305 | static struct attribute *manager_sysfs_attrs[] = { | 
|  | 306 | &manager_attr_name.attr, | 
|  | 307 | &manager_attr_display.attr, | 
|  | 308 | &manager_attr_default_color.attr, | 
|  | 309 | &manager_attr_trans_key_type.attr, | 
|  | 310 | &manager_attr_trans_key_value.attr, | 
|  | 311 | &manager_attr_trans_key_enabled.attr, | 
|  | 312 | &manager_attr_alpha_blending_enabled.attr, | 
|  | 313 | NULL | 
|  | 314 | }; | 
|  | 315 |  | 
|  | 316 | static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, | 
|  | 317 | char *buf) | 
|  | 318 | { | 
|  | 319 | struct omap_overlay_manager *manager; | 
|  | 320 | struct manager_attribute *manager_attr; | 
|  | 321 |  | 
|  | 322 | manager = container_of(kobj, struct omap_overlay_manager, kobj); | 
|  | 323 | manager_attr = container_of(attr, struct manager_attribute, attr); | 
|  | 324 |  | 
|  | 325 | if (!manager_attr->show) | 
|  | 326 | return -ENOENT; | 
|  | 327 |  | 
|  | 328 | return manager_attr->show(manager, buf); | 
|  | 329 | } | 
|  | 330 |  | 
|  | 331 | static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, | 
|  | 332 | const char *buf, size_t size) | 
|  | 333 | { | 
|  | 334 | struct omap_overlay_manager *manager; | 
|  | 335 | struct manager_attribute *manager_attr; | 
|  | 336 |  | 
|  | 337 | manager = container_of(kobj, struct omap_overlay_manager, kobj); | 
|  | 338 | manager_attr = container_of(attr, struct manager_attribute, attr); | 
|  | 339 |  | 
|  | 340 | if (!manager_attr->store) | 
|  | 341 | return -ENOENT; | 
|  | 342 |  | 
|  | 343 | return manager_attr->store(manager, buf, size); | 
|  | 344 | } | 
|  | 345 |  | 
| Emese Revfy | 52cf25d | 2010-01-19 02:58:23 +0100 | [diff] [blame] | 346 | static const struct sysfs_ops manager_sysfs_ops = { | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 347 | .show = manager_attr_show, | 
|  | 348 | .store = manager_attr_store, | 
|  | 349 | }; | 
|  | 350 |  | 
|  | 351 | static struct kobj_type manager_ktype = { | 
|  | 352 | .sysfs_ops = &manager_sysfs_ops, | 
|  | 353 | .default_attrs = manager_sysfs_attrs, | 
|  | 354 | }; | 
|  | 355 |  | 
|  | 356 | /* | 
|  | 357 | * We have 4 levels of cache for the dispc settings. First two are in SW and | 
|  | 358 | * the latter two in HW. | 
|  | 359 | * | 
|  | 360 | * +--------------------+ | 
|  | 361 | * |overlay/manager_info| | 
|  | 362 | * +--------------------+ | 
|  | 363 | *          v | 
|  | 364 | *        apply() | 
|  | 365 | *          v | 
|  | 366 | * +--------------------+ | 
|  | 367 | * |     dss_cache      | | 
|  | 368 | * +--------------------+ | 
|  | 369 | *          v | 
|  | 370 | *      configure() | 
|  | 371 | *          v | 
|  | 372 | * +--------------------+ | 
|  | 373 | * |  shadow registers  | | 
|  | 374 | * +--------------------+ | 
|  | 375 | *          v | 
|  | 376 | * VFP or lcd/digit_enable | 
|  | 377 | *          v | 
|  | 378 | * +--------------------+ | 
|  | 379 | * |      registers     | | 
|  | 380 | * +--------------------+ | 
|  | 381 | */ | 
|  | 382 |  | 
|  | 383 | struct overlay_cache_data { | 
|  | 384 | /* If true, cache changed, but not written to shadow registers. Set | 
|  | 385 | * in apply(), cleared when registers written. */ | 
|  | 386 | bool dirty; | 
|  | 387 | /* If true, shadow registers contain changed values not yet in real | 
|  | 388 | * registers. Set when writing to shadow registers, cleared at | 
|  | 389 | * VSYNC/EVSYNC */ | 
|  | 390 | bool shadow_dirty; | 
|  | 391 |  | 
|  | 392 | bool enabled; | 
|  | 393 |  | 
|  | 394 | u32 paddr; | 
|  | 395 | void __iomem *vaddr; | 
| Amber Jain | 0d66cbb | 2011-05-19 19:47:54 +0530 | [diff] [blame] | 396 | u32 p_uv_addr; /* relevant for NV12 format only */ | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 397 | u16 screen_width; | 
|  | 398 | u16 width; | 
|  | 399 | u16 height; | 
|  | 400 | enum omap_color_mode color_mode; | 
|  | 401 | u8 rotation; | 
|  | 402 | enum omap_dss_rotation_type rotation_type; | 
|  | 403 | bool mirror; | 
|  | 404 |  | 
|  | 405 | u16 pos_x; | 
|  | 406 | u16 pos_y; | 
|  | 407 | u16 out_width;	/* if 0, out_width == width */ | 
|  | 408 | u16 out_height;	/* if 0, out_height == height */ | 
|  | 409 | u8 global_alpha; | 
| Rajkumar N | fd28a39 | 2010-11-04 12:28:42 +0100 | [diff] [blame] | 410 | u8 pre_mult_alpha; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 411 |  | 
|  | 412 | enum omap_channel channel; | 
|  | 413 | bool replication; | 
|  | 414 | bool ilace; | 
|  | 415 |  | 
|  | 416 | enum omap_burst_size burst_size; | 
|  | 417 | u32 fifo_low; | 
|  | 418 | u32 fifo_high; | 
|  | 419 |  | 
|  | 420 | bool manual_update; | 
|  | 421 | }; | 
|  | 422 |  | 
|  | 423 | struct manager_cache_data { | 
|  | 424 | /* If true, cache changed, but not written to shadow registers. Set | 
|  | 425 | * in apply(), cleared when registers written. */ | 
|  | 426 | bool dirty; | 
|  | 427 | /* If true, shadow registers contain changed values not yet in real | 
|  | 428 | * registers. Set when writing to shadow registers, cleared at | 
|  | 429 | * VSYNC/EVSYNC */ | 
|  | 430 | bool shadow_dirty; | 
|  | 431 |  | 
|  | 432 | u32 default_color; | 
|  | 433 |  | 
|  | 434 | enum omap_dss_trans_key_type trans_key_type; | 
|  | 435 | u32 trans_key; | 
|  | 436 | bool trans_enabled; | 
|  | 437 |  | 
|  | 438 | bool alpha_enabled; | 
|  | 439 |  | 
|  | 440 | bool manual_upd_display; | 
|  | 441 | bool manual_update; | 
|  | 442 | bool do_manual_update; | 
|  | 443 |  | 
|  | 444 | /* manual update region */ | 
|  | 445 | u16 x, y, w, h; | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 446 |  | 
|  | 447 | /* enlarge the update area if the update area contains scaled | 
|  | 448 | * overlays */ | 
|  | 449 | bool enlarge_update_area; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 450 | }; | 
|  | 451 |  | 
|  | 452 | static struct { | 
|  | 453 | spinlock_t lock; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 454 | struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS]; | 
|  | 455 | struct manager_cache_data manager_cache[MAX_DSS_MANAGERS]; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 456 |  | 
|  | 457 | bool irq_enabled; | 
|  | 458 | } dss_cache; | 
|  | 459 |  | 
|  | 460 |  | 
|  | 461 |  | 
|  | 462 | static int omap_dss_set_device(struct omap_overlay_manager *mgr, | 
|  | 463 | struct omap_dss_device *dssdev) | 
|  | 464 | { | 
|  | 465 | int i; | 
|  | 466 | int r; | 
|  | 467 |  | 
|  | 468 | if (dssdev->manager) { | 
|  | 469 | DSSERR("display '%s' already has a manager '%s'\n", | 
|  | 470 | dssdev->name, dssdev->manager->name); | 
|  | 471 | return -EINVAL; | 
|  | 472 | } | 
|  | 473 |  | 
|  | 474 | if ((mgr->supported_displays & dssdev->type) == 0) { | 
|  | 475 | DSSERR("display '%s' does not support manager '%s'\n", | 
|  | 476 | dssdev->name, mgr->name); | 
|  | 477 | return -EINVAL; | 
|  | 478 | } | 
|  | 479 |  | 
|  | 480 | for (i = 0; i < mgr->num_overlays; i++) { | 
|  | 481 | struct omap_overlay *ovl = mgr->overlays[i]; | 
|  | 482 |  | 
|  | 483 | if (ovl->manager != mgr || !ovl->info.enabled) | 
|  | 484 | continue; | 
|  | 485 |  | 
|  | 486 | r = dss_check_overlay(ovl, dssdev); | 
|  | 487 | if (r) | 
|  | 488 | return r; | 
|  | 489 | } | 
|  | 490 |  | 
|  | 491 | dssdev->manager = mgr; | 
|  | 492 | mgr->device = dssdev; | 
|  | 493 | mgr->device_changed = true; | 
|  | 494 |  | 
|  | 495 | return 0; | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | static int omap_dss_unset_device(struct omap_overlay_manager *mgr) | 
|  | 499 | { | 
|  | 500 | if (!mgr->device) { | 
|  | 501 | DSSERR("failed to unset display, display not set.\n"); | 
|  | 502 | return -EINVAL; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | mgr->device->manager = NULL; | 
|  | 506 | mgr->device = NULL; | 
|  | 507 | mgr->device_changed = true; | 
|  | 508 |  | 
|  | 509 | return 0; | 
|  | 510 | } | 
|  | 511 |  | 
| Tomi Valkeinen | 3f71cbe | 2010-01-08 17:06:04 +0200 | [diff] [blame] | 512 | static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) | 
|  | 513 | { | 
|  | 514 | unsigned long timeout = msecs_to_jiffies(500); | 
|  | 515 | u32 irq; | 
|  | 516 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 517 | if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { | 
| Tomi Valkeinen | 3f71cbe | 2010-01-08 17:06:04 +0200 | [diff] [blame] | 518 | irq = DISPC_IRQ_EVSYNC_ODD; | 
| Mythri P K | b119601 | 2011-03-08 17:15:54 +0530 | [diff] [blame] | 519 | } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) { | 
|  | 520 | irq = DISPC_IRQ_EVSYNC_EVEN; | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 521 | } else { | 
|  | 522 | if (mgr->id == OMAP_DSS_CHANNEL_LCD) | 
|  | 523 | irq = DISPC_IRQ_VSYNC; | 
|  | 524 | else | 
|  | 525 | irq = DISPC_IRQ_VSYNC2; | 
|  | 526 | } | 
| Tomi Valkeinen | 3f71cbe | 2010-01-08 17:06:04 +0200 | [diff] [blame] | 527 | return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | 
|  | 528 | } | 
|  | 529 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 530 | static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) | 
|  | 531 | { | 
|  | 532 | unsigned long timeout = msecs_to_jiffies(500); | 
|  | 533 | struct manager_cache_data *mc; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 534 | u32 irq; | 
|  | 535 | int r; | 
|  | 536 | int i; | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 537 | struct omap_dss_device *dssdev = mgr->device; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 538 |  | 
| Ville Syrjälä | a74b260 | 2010-03-04 16:03:56 +0200 | [diff] [blame] | 539 | if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 540 | return 0; | 
|  | 541 |  | 
| Mythri P K | b119601 | 2011-03-08 17:15:54 +0530 | [diff] [blame] | 542 | if (dssdev->type == OMAP_DISPLAY_TYPE_VENC | 
|  | 543 | || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 544 | irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 545 | } else { | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 546 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 547 | enum omap_dss_update_mode mode; | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 548 | mode = dssdev->driver->get_update_mode(dssdev); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 549 | if (mode != OMAP_DSS_UPDATE_AUTO) | 
|  | 550 | return 0; | 
|  | 551 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 552 | irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? | 
|  | 553 | DISPC_IRQ_FRAMEDONE | 
|  | 554 | : DISPC_IRQ_FRAMEDONE2; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 555 | } else { | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 556 | irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? | 
|  | 557 | DISPC_IRQ_VSYNC | 
|  | 558 | : DISPC_IRQ_VSYNC2; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 559 | } | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 560 | } | 
|  | 561 |  | 
|  | 562 | mc = &dss_cache.manager_cache[mgr->id]; | 
|  | 563 | i = 0; | 
|  | 564 | while (1) { | 
|  | 565 | unsigned long flags; | 
|  | 566 | bool shadow_dirty, dirty; | 
|  | 567 |  | 
|  | 568 | spin_lock_irqsave(&dss_cache.lock, flags); | 
|  | 569 | dirty = mc->dirty; | 
|  | 570 | shadow_dirty = mc->shadow_dirty; | 
|  | 571 | spin_unlock_irqrestore(&dss_cache.lock, flags); | 
|  | 572 |  | 
|  | 573 | if (!dirty && !shadow_dirty) { | 
|  | 574 | r = 0; | 
|  | 575 | break; | 
|  | 576 | } | 
|  | 577 |  | 
|  | 578 | /* 4 iterations is the worst case: | 
|  | 579 | * 1 - initial iteration, dirty = true (between VFP and VSYNC) | 
|  | 580 | * 2 - first VSYNC, dirty = true | 
|  | 581 | * 3 - dirty = false, shadow_dirty = true | 
|  | 582 | * 4 - shadow_dirty = false */ | 
|  | 583 | if (i++ == 3) { | 
|  | 584 | DSSERR("mgr(%d)->wait_for_go() not finishing\n", | 
|  | 585 | mgr->id); | 
|  | 586 | r = 0; | 
|  | 587 | break; | 
|  | 588 | } | 
|  | 589 |  | 
|  | 590 | r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | 
|  | 591 | if (r == -ERESTARTSYS) | 
|  | 592 | break; | 
|  | 593 |  | 
|  | 594 | if (r) { | 
|  | 595 | DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); | 
|  | 596 | break; | 
|  | 597 | } | 
|  | 598 | } | 
|  | 599 |  | 
|  | 600 | return r; | 
|  | 601 | } | 
|  | 602 |  | 
|  | 603 | int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) | 
|  | 604 | { | 
|  | 605 | unsigned long timeout = msecs_to_jiffies(500); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 606 | struct overlay_cache_data *oc; | 
|  | 607 | struct omap_dss_device *dssdev; | 
|  | 608 | u32 irq; | 
|  | 609 | int r; | 
|  | 610 | int i; | 
|  | 611 |  | 
| Ville Syrjälä | a74b260 | 2010-03-04 16:03:56 +0200 | [diff] [blame] | 612 | if (!ovl->manager) | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 613 | return 0; | 
|  | 614 |  | 
|  | 615 | dssdev = ovl->manager->device; | 
|  | 616 |  | 
| Ville Syrjälä | a74b260 | 2010-03-04 16:03:56 +0200 | [diff] [blame] | 617 | if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | 
|  | 618 | return 0; | 
|  | 619 |  | 
| Mythri P K | b119601 | 2011-03-08 17:15:54 +0530 | [diff] [blame] | 620 | if (dssdev->type == OMAP_DISPLAY_TYPE_VENC | 
|  | 621 | || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 622 | irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 623 | } else { | 
|  | 624 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | 
|  | 625 | enum omap_dss_update_mode mode; | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 626 | mode = dssdev->driver->get_update_mode(dssdev); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 627 | if (mode != OMAP_DSS_UPDATE_AUTO) | 
|  | 628 | return 0; | 
|  | 629 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 630 | irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? | 
|  | 631 | DISPC_IRQ_FRAMEDONE | 
|  | 632 | : DISPC_IRQ_FRAMEDONE2; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 633 | } else { | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 634 | irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? | 
|  | 635 | DISPC_IRQ_VSYNC | 
|  | 636 | : DISPC_IRQ_VSYNC2; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 637 | } | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 638 | } | 
|  | 639 |  | 
|  | 640 | oc = &dss_cache.overlay_cache[ovl->id]; | 
|  | 641 | i = 0; | 
|  | 642 | while (1) { | 
|  | 643 | unsigned long flags; | 
|  | 644 | bool shadow_dirty, dirty; | 
|  | 645 |  | 
|  | 646 | spin_lock_irqsave(&dss_cache.lock, flags); | 
|  | 647 | dirty = oc->dirty; | 
|  | 648 | shadow_dirty = oc->shadow_dirty; | 
|  | 649 | spin_unlock_irqrestore(&dss_cache.lock, flags); | 
|  | 650 |  | 
|  | 651 | if (!dirty && !shadow_dirty) { | 
|  | 652 | r = 0; | 
|  | 653 | break; | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | /* 4 iterations is the worst case: | 
|  | 657 | * 1 - initial iteration, dirty = true (between VFP and VSYNC) | 
|  | 658 | * 2 - first VSYNC, dirty = true | 
|  | 659 | * 3 - dirty = false, shadow_dirty = true | 
|  | 660 | * 4 - shadow_dirty = false */ | 
|  | 661 | if (i++ == 3) { | 
|  | 662 | DSSERR("ovl(%d)->wait_for_go() not finishing\n", | 
|  | 663 | ovl->id); | 
|  | 664 | r = 0; | 
|  | 665 | break; | 
|  | 666 | } | 
|  | 667 |  | 
|  | 668 | r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); | 
|  | 669 | if (r == -ERESTARTSYS) | 
|  | 670 | break; | 
|  | 671 |  | 
|  | 672 | if (r) { | 
|  | 673 | DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); | 
|  | 674 | break; | 
|  | 675 | } | 
|  | 676 | } | 
|  | 677 |  | 
|  | 678 | return r; | 
|  | 679 | } | 
|  | 680 |  | 
|  | 681 | static int overlay_enabled(struct omap_overlay *ovl) | 
|  | 682 | { | 
|  | 683 | return ovl->info.enabled && ovl->manager && ovl->manager->device; | 
|  | 684 | } | 
|  | 685 |  | 
|  | 686 | /* Is rect1 a subset of rect2? */ | 
|  | 687 | static bool rectangle_subset(int x1, int y1, int w1, int h1, | 
|  | 688 | int x2, int y2, int w2, int h2) | 
|  | 689 | { | 
|  | 690 | if (x1 < x2 || y1 < y2) | 
|  | 691 | return false; | 
|  | 692 |  | 
|  | 693 | if (x1 + w1 > x2 + w2) | 
|  | 694 | return false; | 
|  | 695 |  | 
|  | 696 | if (y1 + h1 > y2 + h2) | 
|  | 697 | return false; | 
|  | 698 |  | 
|  | 699 | return true; | 
|  | 700 | } | 
|  | 701 |  | 
|  | 702 | /* Do rect1 and rect2 overlap? */ | 
|  | 703 | static bool rectangle_intersects(int x1, int y1, int w1, int h1, | 
|  | 704 | int x2, int y2, int w2, int h2) | 
|  | 705 | { | 
|  | 706 | if (x1 >= x2 + w2) | 
|  | 707 | return false; | 
|  | 708 |  | 
|  | 709 | if (x2 >= x1 + w1) | 
|  | 710 | return false; | 
|  | 711 |  | 
|  | 712 | if (y1 >= y2 + h2) | 
|  | 713 | return false; | 
|  | 714 |  | 
|  | 715 | if (y2 >= y1 + h1) | 
|  | 716 | return false; | 
|  | 717 |  | 
|  | 718 | return true; | 
|  | 719 | } | 
|  | 720 |  | 
|  | 721 | static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) | 
|  | 722 | { | 
|  | 723 | if (oc->out_width != 0 && oc->width != oc->out_width) | 
|  | 724 | return true; | 
|  | 725 |  | 
|  | 726 | if (oc->out_height != 0 && oc->height != oc->out_height) | 
|  | 727 | return true; | 
|  | 728 |  | 
|  | 729 | return false; | 
|  | 730 | } | 
|  | 731 |  | 
|  | 732 | static int configure_overlay(enum omap_plane plane) | 
|  | 733 | { | 
|  | 734 | struct overlay_cache_data *c; | 
|  | 735 | struct manager_cache_data *mc; | 
|  | 736 | u16 outw, outh; | 
|  | 737 | u16 x, y, w, h; | 
|  | 738 | u32 paddr; | 
|  | 739 | int r; | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 740 | u16 orig_w, orig_h, orig_outw, orig_outh; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 741 |  | 
|  | 742 | DSSDBGF("%d", plane); | 
|  | 743 |  | 
|  | 744 | c = &dss_cache.overlay_cache[plane]; | 
|  | 745 |  | 
|  | 746 | if (!c->enabled) { | 
|  | 747 | dispc_enable_plane(plane, 0); | 
|  | 748 | return 0; | 
|  | 749 | } | 
|  | 750 |  | 
|  | 751 | mc = &dss_cache.manager_cache[c->channel]; | 
|  | 752 |  | 
|  | 753 | x = c->pos_x; | 
|  | 754 | y = c->pos_y; | 
|  | 755 | w = c->width; | 
|  | 756 | h = c->height; | 
|  | 757 | outw = c->out_width == 0 ? c->width : c->out_width; | 
|  | 758 | outh = c->out_height == 0 ? c->height : c->out_height; | 
|  | 759 | paddr = c->paddr; | 
|  | 760 |  | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 761 | orig_w = w; | 
|  | 762 | orig_h = h; | 
|  | 763 | orig_outw = outw; | 
|  | 764 | orig_outh = outh; | 
|  | 765 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 766 | if (c->manual_update && mc->do_manual_update) { | 
|  | 767 | unsigned bpp; | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 768 | unsigned scale_x_m = w, scale_x_d = outw; | 
|  | 769 | unsigned scale_y_m = h, scale_y_d = outh; | 
|  | 770 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 771 | /* If the overlay is outside the update region, disable it */ | 
|  | 772 | if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, | 
|  | 773 | x, y, outw, outh)) { | 
|  | 774 | dispc_enable_plane(plane, 0); | 
|  | 775 | return 0; | 
|  | 776 | } | 
|  | 777 |  | 
|  | 778 | switch (c->color_mode) { | 
| Amber Jain | f20e422 | 2011-05-19 19:47:50 +0530 | [diff] [blame] | 779 | case OMAP_DSS_COLOR_NV12: | 
|  | 780 | bpp = 8; | 
|  | 781 | break; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 782 | case OMAP_DSS_COLOR_RGB16: | 
|  | 783 | case OMAP_DSS_COLOR_ARGB16: | 
|  | 784 | case OMAP_DSS_COLOR_YUV2: | 
|  | 785 | case OMAP_DSS_COLOR_UYVY: | 
| Amber Jain | f20e422 | 2011-05-19 19:47:50 +0530 | [diff] [blame] | 786 | case OMAP_DSS_COLOR_RGBA16: | 
|  | 787 | case OMAP_DSS_COLOR_RGBX16: | 
|  | 788 | case OMAP_DSS_COLOR_ARGB16_1555: | 
|  | 789 | case OMAP_DSS_COLOR_XRGB16_1555: | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 790 | bpp = 16; | 
|  | 791 | break; | 
|  | 792 |  | 
|  | 793 | case OMAP_DSS_COLOR_RGB24P: | 
|  | 794 | bpp = 24; | 
|  | 795 | break; | 
|  | 796 |  | 
|  | 797 | case OMAP_DSS_COLOR_RGB24U: | 
|  | 798 | case OMAP_DSS_COLOR_ARGB32: | 
|  | 799 | case OMAP_DSS_COLOR_RGBA32: | 
|  | 800 | case OMAP_DSS_COLOR_RGBX32: | 
|  | 801 | bpp = 32; | 
|  | 802 | break; | 
|  | 803 |  | 
|  | 804 | default: | 
|  | 805 | BUG(); | 
|  | 806 | } | 
|  | 807 |  | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 808 | if (mc->x > c->pos_x) { | 
|  | 809 | x = 0; | 
|  | 810 | outw -= (mc->x - c->pos_x); | 
|  | 811 | paddr += (mc->x - c->pos_x) * | 
|  | 812 | scale_x_m / scale_x_d * bpp / 8; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 813 | } else { | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 814 | x = c->pos_x - mc->x; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 815 | } | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 816 |  | 
|  | 817 | if (mc->y > c->pos_y) { | 
|  | 818 | y = 0; | 
|  | 819 | outh -= (mc->y - c->pos_y); | 
|  | 820 | paddr += (mc->y - c->pos_y) * | 
|  | 821 | scale_y_m / scale_y_d * | 
|  | 822 | c->screen_width * bpp / 8; | 
|  | 823 | } else { | 
|  | 824 | y = c->pos_y - mc->y; | 
|  | 825 | } | 
|  | 826 |  | 
|  | 827 | if (mc->w < (x + outw)) | 
|  | 828 | outw -= (x + outw) - (mc->w); | 
|  | 829 |  | 
|  | 830 | if (mc->h < (y + outh)) | 
|  | 831 | outh -= (y + outh) - (mc->h); | 
|  | 832 |  | 
|  | 833 | w = w * outw / orig_outw; | 
|  | 834 | h = h * outh / orig_outh; | 
| Tomi Valkeinen | f55fdcf | 2010-06-03 16:27:46 +0300 | [diff] [blame] | 835 |  | 
|  | 836 | /* YUV mode overlay's input width has to be even and the | 
|  | 837 | * algorithm above may adjust the width to be odd. | 
|  | 838 | * | 
|  | 839 | * Here we adjust the width if needed, preferring to increase | 
|  | 840 | * the width if the original width was bigger. | 
|  | 841 | */ | 
|  | 842 | if ((w & 1) && | 
|  | 843 | (c->color_mode == OMAP_DSS_COLOR_YUV2 || | 
|  | 844 | c->color_mode == OMAP_DSS_COLOR_UYVY)) { | 
|  | 845 | if (orig_w > w) | 
|  | 846 | w += 1; | 
|  | 847 | else | 
|  | 848 | w -= 1; | 
|  | 849 | } | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 850 | } | 
|  | 851 |  | 
|  | 852 | r = dispc_setup_plane(plane, | 
|  | 853 | paddr, | 
|  | 854 | c->screen_width, | 
|  | 855 | x, y, | 
|  | 856 | w, h, | 
|  | 857 | outw, outh, | 
|  | 858 | c->color_mode, | 
|  | 859 | c->ilace, | 
|  | 860 | c->rotation_type, | 
|  | 861 | c->rotation, | 
|  | 862 | c->mirror, | 
| Rajkumar N | fd28a39 | 2010-11-04 12:28:42 +0100 | [diff] [blame] | 863 | c->global_alpha, | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 864 | c->pre_mult_alpha, | 
| Amber Jain | 0d66cbb | 2011-05-19 19:47:54 +0530 | [diff] [blame] | 865 | c->channel, | 
|  | 866 | c->p_uv_addr); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 867 |  | 
|  | 868 | if (r) { | 
|  | 869 | /* this shouldn't happen */ | 
|  | 870 | DSSERR("dispc_setup_plane failed for ovl %d\n", plane); | 
|  | 871 | dispc_enable_plane(plane, 0); | 
|  | 872 | return r; | 
|  | 873 | } | 
|  | 874 |  | 
|  | 875 | dispc_enable_replication(plane, c->replication); | 
|  | 876 |  | 
|  | 877 | dispc_set_burst_size(plane, c->burst_size); | 
|  | 878 | dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); | 
|  | 879 |  | 
|  | 880 | dispc_enable_plane(plane, 1); | 
|  | 881 |  | 
|  | 882 | return 0; | 
|  | 883 | } | 
|  | 884 |  | 
|  | 885 | static void configure_manager(enum omap_channel channel) | 
|  | 886 | { | 
|  | 887 | struct manager_cache_data *c; | 
|  | 888 |  | 
|  | 889 | DSSDBGF("%d", channel); | 
|  | 890 |  | 
|  | 891 | c = &dss_cache.manager_cache[channel]; | 
|  | 892 |  | 
| Carlos Lopez | a3bb67a | 2010-04-25 12:53:35 +0200 | [diff] [blame] | 893 | dispc_set_default_color(channel, c->default_color); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 894 | dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); | 
|  | 895 | dispc_enable_trans_key(channel, c->trans_enabled); | 
|  | 896 | dispc_enable_alpha_blending(channel, c->alpha_enabled); | 
|  | 897 | } | 
|  | 898 |  | 
|  | 899 | /* configure_dispc() tries to write values from cache to shadow registers. | 
|  | 900 | * It writes only to those managers/overlays that are not busy. | 
|  | 901 | * returns 0 if everything could be written to shadow registers. | 
|  | 902 | * returns 1 if not everything could be written to shadow registers. */ | 
|  | 903 | static int configure_dispc(void) | 
|  | 904 | { | 
|  | 905 | struct overlay_cache_data *oc; | 
|  | 906 | struct manager_cache_data *mc; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 907 | const int num_ovls = dss_feat_get_num_ovls(); | 
|  | 908 | const int num_mgrs = dss_feat_get_num_mgrs(); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 909 | int i; | 
|  | 910 | int r; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 911 | bool mgr_busy[MAX_DSS_MANAGERS]; | 
|  | 912 | bool mgr_go[MAX_DSS_MANAGERS]; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 913 | bool busy; | 
|  | 914 |  | 
|  | 915 | r = 0; | 
|  | 916 | busy = false; | 
|  | 917 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 918 | for (i = 0; i < num_mgrs; i++) { | 
|  | 919 | mgr_busy[i] = dispc_go_busy(i); | 
|  | 920 | mgr_go[i] = false; | 
|  | 921 | } | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 922 |  | 
|  | 923 | /* Commit overlay settings */ | 
|  | 924 | for (i = 0; i < num_ovls; ++i) { | 
|  | 925 | oc = &dss_cache.overlay_cache[i]; | 
|  | 926 | mc = &dss_cache.manager_cache[oc->channel]; | 
|  | 927 |  | 
|  | 928 | if (!oc->dirty) | 
|  | 929 | continue; | 
|  | 930 |  | 
|  | 931 | if (oc->manual_update && !mc->do_manual_update) | 
|  | 932 | continue; | 
|  | 933 |  | 
|  | 934 | if (mgr_busy[oc->channel]) { | 
|  | 935 | busy = true; | 
|  | 936 | continue; | 
|  | 937 | } | 
|  | 938 |  | 
|  | 939 | r = configure_overlay(i); | 
|  | 940 | if (r) | 
|  | 941 | DSSERR("configure_overlay %d failed\n", i); | 
|  | 942 |  | 
|  | 943 | oc->dirty = false; | 
|  | 944 | oc->shadow_dirty = true; | 
|  | 945 | mgr_go[oc->channel] = true; | 
|  | 946 | } | 
|  | 947 |  | 
|  | 948 | /* Commit manager settings */ | 
|  | 949 | for (i = 0; i < num_mgrs; ++i) { | 
|  | 950 | mc = &dss_cache.manager_cache[i]; | 
|  | 951 |  | 
|  | 952 | if (!mc->dirty) | 
|  | 953 | continue; | 
|  | 954 |  | 
|  | 955 | if (mc->manual_update && !mc->do_manual_update) | 
|  | 956 | continue; | 
|  | 957 |  | 
|  | 958 | if (mgr_busy[i]) { | 
|  | 959 | busy = true; | 
|  | 960 | continue; | 
|  | 961 | } | 
|  | 962 |  | 
|  | 963 | configure_manager(i); | 
|  | 964 | mc->dirty = false; | 
|  | 965 | mc->shadow_dirty = true; | 
|  | 966 | mgr_go[i] = true; | 
|  | 967 | } | 
|  | 968 |  | 
|  | 969 | /* set GO */ | 
|  | 970 | for (i = 0; i < num_mgrs; ++i) { | 
|  | 971 | mc = &dss_cache.manager_cache[i]; | 
|  | 972 |  | 
|  | 973 | if (!mgr_go[i]) | 
|  | 974 | continue; | 
|  | 975 |  | 
|  | 976 | /* We don't need GO with manual update display. LCD iface will | 
|  | 977 | * always be turned off after frame, and new settings will be | 
|  | 978 | * taken in to use at next update */ | 
|  | 979 | if (!mc->manual_upd_display) | 
|  | 980 | dispc_go(i); | 
|  | 981 | } | 
|  | 982 |  | 
|  | 983 | if (busy) | 
|  | 984 | r = 1; | 
|  | 985 | else | 
|  | 986 | r = 0; | 
|  | 987 |  | 
|  | 988 | return r; | 
|  | 989 | } | 
|  | 990 |  | 
| Tomi Valkeinen | f49a951 | 2010-03-26 16:26:37 +0200 | [diff] [blame] | 991 | /* Make the coordinates even. There are some strange problems with OMAP and | 
|  | 992 | * partial DSI update when the update widths are odd. */ | 
|  | 993 | static void make_even(u16 *x, u16 *w) | 
|  | 994 | { | 
|  | 995 | u16 x1, x2; | 
|  | 996 |  | 
|  | 997 | x1 = *x; | 
|  | 998 | x2 = *x + *w; | 
|  | 999 |  | 
|  | 1000 | x1 &= ~1; | 
|  | 1001 | x2 = ALIGN(x2, 2); | 
|  | 1002 |  | 
|  | 1003 | *x = x1; | 
|  | 1004 | *w = x2 - x1; | 
|  | 1005 | } | 
|  | 1006 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1007 | /* Configure dispc for partial update. Return possibly modified update | 
|  | 1008 | * area */ | 
|  | 1009 | void dss_setup_partial_planes(struct omap_dss_device *dssdev, | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 1010 | u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area) | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1011 | { | 
|  | 1012 | struct overlay_cache_data *oc; | 
|  | 1013 | struct manager_cache_data *mc; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1014 | const int num_ovls = dss_feat_get_num_ovls(); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1015 | struct omap_overlay_manager *mgr; | 
|  | 1016 | int i; | 
|  | 1017 | u16 x, y, w, h; | 
|  | 1018 | unsigned long flags; | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1019 | bool area_changed; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1020 |  | 
|  | 1021 | x = *xi; | 
|  | 1022 | y = *yi; | 
|  | 1023 | w = *wi; | 
|  | 1024 | h = *hi; | 
|  | 1025 |  | 
|  | 1026 | DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", | 
|  | 1027 | *xi, *yi, *wi, *hi); | 
|  | 1028 |  | 
|  | 1029 | mgr = dssdev->manager; | 
|  | 1030 |  | 
|  | 1031 | if (!mgr) { | 
|  | 1032 | DSSDBG("no manager\n"); | 
|  | 1033 | return; | 
|  | 1034 | } | 
|  | 1035 |  | 
| Tomi Valkeinen | f49a951 | 2010-03-26 16:26:37 +0200 | [diff] [blame] | 1036 | make_even(&x, &w); | 
|  | 1037 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1038 | spin_lock_irqsave(&dss_cache.lock, flags); | 
|  | 1039 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1040 | /* | 
|  | 1041 | * Execute the outer loop until the inner loop has completed | 
|  | 1042 | * once without increasing the update area. This will ensure that | 
|  | 1043 | * all scaled overlays end up completely within the update area. | 
|  | 1044 | */ | 
|  | 1045 | do { | 
|  | 1046 | area_changed = false; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1047 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1048 | /* We need to show the whole overlay if it is scaled. So look | 
|  | 1049 | * for those, and make the update area larger if found. | 
|  | 1050 | * Also mark the overlay cache dirty */ | 
|  | 1051 | for (i = 0; i < num_ovls; ++i) { | 
|  | 1052 | unsigned x1, y1, x2, y2; | 
|  | 1053 | unsigned outw, outh; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1054 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1055 | oc = &dss_cache.overlay_cache[i]; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1056 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1057 | if (oc->channel != mgr->id) | 
|  | 1058 | continue; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1059 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1060 | oc->dirty = true; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1061 |  | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 1062 | if (!enlarge_update_area) | 
|  | 1063 | continue; | 
|  | 1064 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1065 | if (!oc->enabled) | 
|  | 1066 | continue; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1067 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1068 | if (!dispc_is_overlay_scaled(oc)) | 
|  | 1069 | continue; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1070 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1071 | outw = oc->out_width == 0 ? | 
|  | 1072 | oc->width : oc->out_width; | 
|  | 1073 | outh = oc->out_height == 0 ? | 
|  | 1074 | oc->height : oc->out_height; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1075 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1076 | /* is the overlay outside the update region? */ | 
|  | 1077 | if (!rectangle_intersects(x, y, w, h, | 
|  | 1078 | oc->pos_x, oc->pos_y, | 
|  | 1079 | outw, outh)) | 
|  | 1080 | continue; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1081 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1082 | /* if the overlay totally inside the update region? */ | 
|  | 1083 | if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, | 
|  | 1084 | x, y, w, h)) | 
|  | 1085 | continue; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1086 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1087 | if (x > oc->pos_x) | 
|  | 1088 | x1 = oc->pos_x; | 
|  | 1089 | else | 
|  | 1090 | x1 = x; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1091 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1092 | if (y > oc->pos_y) | 
|  | 1093 | y1 = oc->pos_y; | 
|  | 1094 | else | 
|  | 1095 | y1 = y; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1096 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1097 | if ((x + w) < (oc->pos_x + outw)) | 
|  | 1098 | x2 = oc->pos_x + outw; | 
|  | 1099 | else | 
|  | 1100 | x2 = x + w; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1101 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1102 | if ((y + h) < (oc->pos_y + outh)) | 
|  | 1103 | y2 = oc->pos_y + outh; | 
|  | 1104 | else | 
|  | 1105 | y2 = y + h; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1106 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1107 | x = x1; | 
|  | 1108 | y = y1; | 
|  | 1109 | w = x2 - x1; | 
|  | 1110 | h = y2 - y1; | 
| Tomi Valkeinen | f49a951 | 2010-03-26 16:26:37 +0200 | [diff] [blame] | 1111 |  | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1112 | make_even(&x, &w); | 
|  | 1113 |  | 
|  | 1114 | DSSDBG("changing upd area due to ovl(%d) " | 
|  | 1115 | "scaling %d,%d %dx%d\n", | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1116 | i, x, y, w, h); | 
| Tomi Valkeinen | 8cab90f | 2010-06-09 15:09:30 +0300 | [diff] [blame] | 1117 |  | 
|  | 1118 | area_changed = true; | 
|  | 1119 | } | 
|  | 1120 | } while (area_changed); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1121 |  | 
|  | 1122 | mc = &dss_cache.manager_cache[mgr->id]; | 
|  | 1123 | mc->do_manual_update = true; | 
| Tomi Valkeinen | 26a8c25 | 2010-06-09 15:31:34 +0300 | [diff] [blame] | 1124 | mc->enlarge_update_area = enlarge_update_area; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1125 | mc->x = x; | 
|  | 1126 | mc->y = y; | 
|  | 1127 | mc->w = w; | 
|  | 1128 | mc->h = h; | 
|  | 1129 |  | 
|  | 1130 | configure_dispc(); | 
|  | 1131 |  | 
|  | 1132 | mc->do_manual_update = false; | 
|  | 1133 |  | 
|  | 1134 | spin_unlock_irqrestore(&dss_cache.lock, flags); | 
|  | 1135 |  | 
|  | 1136 | *xi = x; | 
|  | 1137 | *yi = y; | 
|  | 1138 | *wi = w; | 
|  | 1139 | *hi = h; | 
|  | 1140 | } | 
|  | 1141 |  | 
|  | 1142 | void dss_start_update(struct omap_dss_device *dssdev) | 
|  | 1143 | { | 
|  | 1144 | struct manager_cache_data *mc; | 
|  | 1145 | struct overlay_cache_data *oc; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1146 | const int num_ovls = dss_feat_get_num_ovls(); | 
|  | 1147 | const int num_mgrs = dss_feat_get_num_mgrs(); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1148 | struct omap_overlay_manager *mgr; | 
|  | 1149 | int i; | 
|  | 1150 |  | 
|  | 1151 | mgr = dssdev->manager; | 
|  | 1152 |  | 
|  | 1153 | for (i = 0; i < num_ovls; ++i) { | 
|  | 1154 | oc = &dss_cache.overlay_cache[i]; | 
|  | 1155 | if (oc->channel != mgr->id) | 
|  | 1156 | continue; | 
|  | 1157 |  | 
|  | 1158 | oc->shadow_dirty = false; | 
|  | 1159 | } | 
|  | 1160 |  | 
|  | 1161 | for (i = 0; i < num_mgrs; ++i) { | 
|  | 1162 | mc = &dss_cache.manager_cache[i]; | 
|  | 1163 | if (mgr->id != i) | 
|  | 1164 | continue; | 
|  | 1165 |  | 
|  | 1166 | mc->shadow_dirty = false; | 
|  | 1167 | } | 
|  | 1168 |  | 
| Tomi Valkeinen | a2faee8 | 2010-01-08 17:14:53 +0200 | [diff] [blame] | 1169 | dssdev->manager->enable(dssdev->manager); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1170 | } | 
|  | 1171 |  | 
|  | 1172 | static void dss_apply_irq_handler(void *data, u32 mask) | 
|  | 1173 | { | 
|  | 1174 | struct manager_cache_data *mc; | 
|  | 1175 | struct overlay_cache_data *oc; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1176 | const int num_ovls = dss_feat_get_num_ovls(); | 
|  | 1177 | const int num_mgrs = dss_feat_get_num_mgrs(); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1178 | int i, r; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1179 | bool mgr_busy[MAX_DSS_MANAGERS]; | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1180 | u32 irq_mask; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1181 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1182 | for (i = 0; i < num_mgrs; i++) | 
|  | 1183 | mgr_busy[i] = dispc_go_busy(i); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1184 |  | 
|  | 1185 | spin_lock(&dss_cache.lock); | 
|  | 1186 |  | 
|  | 1187 | for (i = 0; i < num_ovls; ++i) { | 
|  | 1188 | oc = &dss_cache.overlay_cache[i]; | 
|  | 1189 | if (!mgr_busy[oc->channel]) | 
|  | 1190 | oc->shadow_dirty = false; | 
|  | 1191 | } | 
|  | 1192 |  | 
|  | 1193 | for (i = 0; i < num_mgrs; ++i) { | 
|  | 1194 | mc = &dss_cache.manager_cache[i]; | 
|  | 1195 | if (!mgr_busy[i]) | 
|  | 1196 | mc->shadow_dirty = false; | 
|  | 1197 | } | 
|  | 1198 |  | 
|  | 1199 | r = configure_dispc(); | 
|  | 1200 | if (r == 1) | 
|  | 1201 | goto end; | 
|  | 1202 |  | 
|  | 1203 | /* re-read busy flags */ | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1204 | for (i = 0; i < num_mgrs; i++) | 
|  | 1205 | mgr_busy[i] = dispc_go_busy(i); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1206 |  | 
|  | 1207 | /* keep running as long as there are busy managers, so that | 
|  | 1208 | * we can collect overlay-applied information */ | 
|  | 1209 | for (i = 0; i < num_mgrs; ++i) { | 
|  | 1210 | if (mgr_busy[i]) | 
|  | 1211 | goto end; | 
|  | 1212 | } | 
|  | 1213 |  | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1214 | irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | | 
|  | 1215 | DISPC_IRQ_EVSYNC_EVEN; | 
|  | 1216 | if (dss_has_feature(FEAT_MGR_LCD2)) | 
|  | 1217 | irq_mask |= DISPC_IRQ_VSYNC2; | 
|  | 1218 |  | 
|  | 1219 | omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1220 | dss_cache.irq_enabled = false; | 
|  | 1221 |  | 
|  | 1222 | end: | 
|  | 1223 | spin_unlock(&dss_cache.lock); | 
|  | 1224 | } | 
|  | 1225 |  | 
|  | 1226 | static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) | 
|  | 1227 | { | 
|  | 1228 | struct overlay_cache_data *oc; | 
|  | 1229 | struct manager_cache_data *mc; | 
|  | 1230 | int i; | 
|  | 1231 | struct omap_overlay *ovl; | 
|  | 1232 | int num_planes_enabled = 0; | 
|  | 1233 | bool use_fifomerge; | 
|  | 1234 | unsigned long flags; | 
|  | 1235 | int r; | 
|  | 1236 |  | 
|  | 1237 | DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); | 
|  | 1238 |  | 
|  | 1239 | spin_lock_irqsave(&dss_cache.lock, flags); | 
|  | 1240 |  | 
|  | 1241 | /* Configure overlays */ | 
|  | 1242 | for (i = 0; i < omap_dss_get_num_overlays(); ++i) { | 
|  | 1243 | struct omap_dss_device *dssdev; | 
|  | 1244 |  | 
|  | 1245 | ovl = omap_dss_get_overlay(i); | 
|  | 1246 |  | 
|  | 1247 | if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) | 
|  | 1248 | continue; | 
|  | 1249 |  | 
|  | 1250 | oc = &dss_cache.overlay_cache[ovl->id]; | 
|  | 1251 |  | 
|  | 1252 | if (!overlay_enabled(ovl)) { | 
|  | 1253 | if (oc->enabled) { | 
|  | 1254 | oc->enabled = false; | 
|  | 1255 | oc->dirty = true; | 
|  | 1256 | } | 
|  | 1257 | continue; | 
|  | 1258 | } | 
|  | 1259 |  | 
|  | 1260 | if (!ovl->info_dirty) { | 
|  | 1261 | if (oc->enabled) | 
|  | 1262 | ++num_planes_enabled; | 
|  | 1263 | continue; | 
|  | 1264 | } | 
|  | 1265 |  | 
|  | 1266 | dssdev = ovl->manager->device; | 
|  | 1267 |  | 
|  | 1268 | if (dss_check_overlay(ovl, dssdev)) { | 
|  | 1269 | if (oc->enabled) { | 
|  | 1270 | oc->enabled = false; | 
|  | 1271 | oc->dirty = true; | 
|  | 1272 | } | 
|  | 1273 | continue; | 
|  | 1274 | } | 
|  | 1275 |  | 
|  | 1276 | ovl->info_dirty = false; | 
|  | 1277 | oc->dirty = true; | 
|  | 1278 |  | 
|  | 1279 | oc->paddr = ovl->info.paddr; | 
|  | 1280 | oc->vaddr = ovl->info.vaddr; | 
| Amber Jain | 0d66cbb | 2011-05-19 19:47:54 +0530 | [diff] [blame] | 1281 | oc->p_uv_addr = ovl->info.p_uv_addr; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1282 | oc->screen_width = ovl->info.screen_width; | 
|  | 1283 | oc->width = ovl->info.width; | 
|  | 1284 | oc->height = ovl->info.height; | 
|  | 1285 | oc->color_mode = ovl->info.color_mode; | 
|  | 1286 | oc->rotation = ovl->info.rotation; | 
|  | 1287 | oc->rotation_type = ovl->info.rotation_type; | 
|  | 1288 | oc->mirror = ovl->info.mirror; | 
|  | 1289 | oc->pos_x = ovl->info.pos_x; | 
|  | 1290 | oc->pos_y = ovl->info.pos_y; | 
|  | 1291 | oc->out_width = ovl->info.out_width; | 
|  | 1292 | oc->out_height = ovl->info.out_height; | 
|  | 1293 | oc->global_alpha = ovl->info.global_alpha; | 
| Rajkumar N | fd28a39 | 2010-11-04 12:28:42 +0100 | [diff] [blame] | 1294 | oc->pre_mult_alpha = ovl->info.pre_mult_alpha; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1295 |  | 
|  | 1296 | oc->replication = | 
|  | 1297 | dss_use_replication(dssdev, ovl->info.color_mode); | 
|  | 1298 |  | 
|  | 1299 | oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; | 
|  | 1300 |  | 
|  | 1301 | oc->channel = ovl->manager->id; | 
|  | 1302 |  | 
|  | 1303 | oc->enabled = true; | 
|  | 1304 |  | 
|  | 1305 | oc->manual_update = | 
|  | 1306 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 1307 | dssdev->driver->get_update_mode(dssdev) != | 
|  | 1308 | OMAP_DSS_UPDATE_AUTO; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1309 |  | 
|  | 1310 | ++num_planes_enabled; | 
|  | 1311 | } | 
|  | 1312 |  | 
|  | 1313 | /* Configure managers */ | 
|  | 1314 | list_for_each_entry(mgr, &manager_list, list) { | 
|  | 1315 | struct omap_dss_device *dssdev; | 
|  | 1316 |  | 
|  | 1317 | if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) | 
|  | 1318 | continue; | 
|  | 1319 |  | 
|  | 1320 | mc = &dss_cache.manager_cache[mgr->id]; | 
|  | 1321 |  | 
|  | 1322 | if (mgr->device_changed) { | 
|  | 1323 | mgr->device_changed = false; | 
|  | 1324 | mgr->info_dirty  = true; | 
|  | 1325 | } | 
|  | 1326 |  | 
|  | 1327 | if (!mgr->info_dirty) | 
|  | 1328 | continue; | 
|  | 1329 |  | 
|  | 1330 | if (!mgr->device) | 
|  | 1331 | continue; | 
|  | 1332 |  | 
|  | 1333 | dssdev = mgr->device; | 
|  | 1334 |  | 
|  | 1335 | mgr->info_dirty = false; | 
|  | 1336 | mc->dirty = true; | 
|  | 1337 |  | 
|  | 1338 | mc->default_color = mgr->info.default_color; | 
|  | 1339 | mc->trans_key_type = mgr->info.trans_key_type; | 
|  | 1340 | mc->trans_key = mgr->info.trans_key; | 
|  | 1341 | mc->trans_enabled = mgr->info.trans_enabled; | 
|  | 1342 | mc->alpha_enabled = mgr->info.alpha_enabled; | 
|  | 1343 |  | 
|  | 1344 | mc->manual_upd_display = | 
|  | 1345 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; | 
|  | 1346 |  | 
|  | 1347 | mc->manual_update = | 
|  | 1348 | dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && | 
| Tomi Valkeinen | 446f7bf | 2010-01-11 16:12:31 +0200 | [diff] [blame] | 1349 | dssdev->driver->get_update_mode(dssdev) != | 
|  | 1350 | OMAP_DSS_UPDATE_AUTO; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1351 | } | 
|  | 1352 |  | 
|  | 1353 | /* XXX TODO: Try to get fifomerge working. The problem is that it | 
|  | 1354 | * affects both managers, not individually but at the same time. This | 
|  | 1355 | * means the change has to be well synchronized. I guess the proper way | 
|  | 1356 | * is to have a two step process for fifo merge: | 
|  | 1357 | *        fifomerge enable: | 
|  | 1358 | *             1. disable other planes, leaving one plane enabled | 
|  | 1359 | *             2. wait until the planes are disabled on HW | 
|  | 1360 | *             3. config merged fifo thresholds, enable fifomerge | 
|  | 1361 | *        fifomerge disable: | 
|  | 1362 | *             1. config unmerged fifo thresholds, disable fifomerge | 
|  | 1363 | *             2. wait until fifo changes are in HW | 
|  | 1364 | *             3. enable planes | 
|  | 1365 | */ | 
|  | 1366 | use_fifomerge = false; | 
|  | 1367 |  | 
|  | 1368 | /* Configure overlay fifos */ | 
|  | 1369 | for (i = 0; i < omap_dss_get_num_overlays(); ++i) { | 
|  | 1370 | struct omap_dss_device *dssdev; | 
|  | 1371 | u32 size; | 
|  | 1372 |  | 
|  | 1373 | ovl = omap_dss_get_overlay(i); | 
|  | 1374 |  | 
|  | 1375 | if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) | 
|  | 1376 | continue; | 
|  | 1377 |  | 
|  | 1378 | oc = &dss_cache.overlay_cache[ovl->id]; | 
|  | 1379 |  | 
|  | 1380 | if (!oc->enabled) | 
|  | 1381 | continue; | 
|  | 1382 |  | 
|  | 1383 | dssdev = ovl->manager->device; | 
|  | 1384 |  | 
|  | 1385 | size = dispc_get_plane_fifo_size(ovl->id); | 
|  | 1386 | if (use_fifomerge) | 
|  | 1387 | size *= 3; | 
|  | 1388 |  | 
|  | 1389 | switch (dssdev->type) { | 
|  | 1390 | case OMAP_DISPLAY_TYPE_DPI: | 
|  | 1391 | case OMAP_DISPLAY_TYPE_DBI: | 
|  | 1392 | case OMAP_DISPLAY_TYPE_SDI: | 
|  | 1393 | case OMAP_DISPLAY_TYPE_VENC: | 
| Mythri P K | b119601 | 2011-03-08 17:15:54 +0530 | [diff] [blame] | 1394 | case OMAP_DISPLAY_TYPE_HDMI: | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1395 | default_get_overlay_fifo_thresholds(ovl->id, size, | 
|  | 1396 | &oc->burst_size, &oc->fifo_low, | 
|  | 1397 | &oc->fifo_high); | 
|  | 1398 | break; | 
|  | 1399 | #ifdef CONFIG_OMAP2_DSS_DSI | 
|  | 1400 | case OMAP_DISPLAY_TYPE_DSI: | 
|  | 1401 | dsi_get_overlay_fifo_thresholds(ovl->id, size, | 
|  | 1402 | &oc->burst_size, &oc->fifo_low, | 
|  | 1403 | &oc->fifo_high); | 
|  | 1404 | break; | 
|  | 1405 | #endif | 
|  | 1406 | default: | 
|  | 1407 | BUG(); | 
|  | 1408 | } | 
|  | 1409 | } | 
|  | 1410 |  | 
|  | 1411 | r = 0; | 
| Archit Taneja | 6af9cd1 | 2011-01-31 16:27:44 +0000 | [diff] [blame] | 1412 | dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1413 | if (!dss_cache.irq_enabled) { | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1414 | u32 mask; | 
|  | 1415 |  | 
|  | 1416 | mask = DISPC_IRQ_VSYNC	| DISPC_IRQ_EVSYNC_ODD | | 
|  | 1417 | DISPC_IRQ_EVSYNC_EVEN; | 
|  | 1418 | if (dss_has_feature(FEAT_MGR_LCD2)) | 
|  | 1419 | mask |= DISPC_IRQ_VSYNC2; | 
|  | 1420 |  | 
|  | 1421 | r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1422 | dss_cache.irq_enabled = true; | 
|  | 1423 | } | 
|  | 1424 | configure_dispc(); | 
| Archit Taneja | 6af9cd1 | 2011-01-31 16:27:44 +0000 | [diff] [blame] | 1425 | dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1426 |  | 
|  | 1427 | spin_unlock_irqrestore(&dss_cache.lock, flags); | 
|  | 1428 |  | 
|  | 1429 | return r; | 
|  | 1430 | } | 
|  | 1431 |  | 
|  | 1432 | static int dss_check_manager(struct omap_overlay_manager *mgr) | 
|  | 1433 | { | 
|  | 1434 | /* OMAP supports only graphics source transparency color key and alpha | 
|  | 1435 | * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ | 
|  | 1436 |  | 
|  | 1437 | if (mgr->info.alpha_enabled && mgr->info.trans_enabled && | 
|  | 1438 | mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) | 
|  | 1439 | return -EINVAL; | 
|  | 1440 |  | 
|  | 1441 | return 0; | 
|  | 1442 | } | 
|  | 1443 |  | 
|  | 1444 | static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, | 
|  | 1445 | struct omap_overlay_manager_info *info) | 
|  | 1446 | { | 
|  | 1447 | int r; | 
|  | 1448 | struct omap_overlay_manager_info old_info; | 
|  | 1449 |  | 
|  | 1450 | old_info = mgr->info; | 
|  | 1451 | mgr->info = *info; | 
|  | 1452 |  | 
|  | 1453 | r = dss_check_manager(mgr); | 
|  | 1454 | if (r) { | 
|  | 1455 | mgr->info = old_info; | 
|  | 1456 | return r; | 
|  | 1457 | } | 
|  | 1458 |  | 
|  | 1459 | mgr->info_dirty = true; | 
|  | 1460 |  | 
|  | 1461 | return 0; | 
|  | 1462 | } | 
|  | 1463 |  | 
|  | 1464 | static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, | 
|  | 1465 | struct omap_overlay_manager_info *info) | 
|  | 1466 | { | 
|  | 1467 | *info = mgr->info; | 
|  | 1468 | } | 
|  | 1469 |  | 
| Tomi Valkeinen | a2faee8 | 2010-01-08 17:14:53 +0200 | [diff] [blame] | 1470 | static int dss_mgr_enable(struct omap_overlay_manager *mgr) | 
|  | 1471 | { | 
|  | 1472 | dispc_enable_channel(mgr->id, 1); | 
|  | 1473 | return 0; | 
|  | 1474 | } | 
|  | 1475 |  | 
|  | 1476 | static int dss_mgr_disable(struct omap_overlay_manager *mgr) | 
|  | 1477 | { | 
|  | 1478 | dispc_enable_channel(mgr->id, 0); | 
|  | 1479 | return 0; | 
|  | 1480 | } | 
|  | 1481 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1482 | static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) | 
|  | 1483 | { | 
|  | 1484 | ++num_managers; | 
|  | 1485 | list_add_tail(&manager->list, &manager_list); | 
|  | 1486 | } | 
|  | 1487 |  | 
|  | 1488 | int dss_init_overlay_managers(struct platform_device *pdev) | 
|  | 1489 | { | 
|  | 1490 | int i, r; | 
|  | 1491 |  | 
|  | 1492 | spin_lock_init(&dss_cache.lock); | 
|  | 1493 |  | 
|  | 1494 | INIT_LIST_HEAD(&manager_list); | 
|  | 1495 |  | 
|  | 1496 | num_managers = 0; | 
|  | 1497 |  | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1498 | for (i = 0; i < dss_feat_get_num_mgrs(); ++i) { | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1499 | struct omap_overlay_manager *mgr; | 
|  | 1500 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | 
|  | 1501 |  | 
|  | 1502 | BUG_ON(mgr == NULL); | 
|  | 1503 |  | 
|  | 1504 | switch (i) { | 
|  | 1505 | case 0: | 
|  | 1506 | mgr->name = "lcd"; | 
|  | 1507 | mgr->id = OMAP_DSS_CHANNEL_LCD; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1508 | break; | 
|  | 1509 | case 1: | 
|  | 1510 | mgr->name = "tv"; | 
|  | 1511 | mgr->id = OMAP_DSS_CHANNEL_DIGIT; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1512 | break; | 
| Sumit Semwal | 18faa1b | 2010-12-02 11:27:14 +0000 | [diff] [blame] | 1513 | case 2: | 
|  | 1514 | mgr->name = "lcd2"; | 
|  | 1515 | mgr->id = OMAP_DSS_CHANNEL_LCD2; | 
|  | 1516 | break; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1517 | } | 
|  | 1518 |  | 
|  | 1519 | mgr->set_device = &omap_dss_set_device; | 
|  | 1520 | mgr->unset_device = &omap_dss_unset_device; | 
|  | 1521 | mgr->apply = &omap_dss_mgr_apply; | 
|  | 1522 | mgr->set_manager_info = &omap_dss_mgr_set_info; | 
|  | 1523 | mgr->get_manager_info = &omap_dss_mgr_get_info; | 
|  | 1524 | mgr->wait_for_go = &dss_mgr_wait_for_go; | 
| Tomi Valkeinen | 3f71cbe | 2010-01-08 17:06:04 +0200 | [diff] [blame] | 1525 | mgr->wait_for_vsync = &dss_mgr_wait_for_vsync; | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1526 |  | 
| Tomi Valkeinen | a2faee8 | 2010-01-08 17:14:53 +0200 | [diff] [blame] | 1527 | mgr->enable = &dss_mgr_enable; | 
|  | 1528 | mgr->disable = &dss_mgr_disable; | 
|  | 1529 |  | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1530 | mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; | 
| Archit Taneja | a0acb55 | 2010-09-15 19:20:00 +0530 | [diff] [blame] | 1531 | mgr->supported_displays = | 
|  | 1532 | dss_feat_get_supported_displays(mgr->id); | 
| Tomi Valkeinen | eed07e0 | 2009-08-07 13:43:20 +0300 | [diff] [blame] | 1533 |  | 
|  | 1534 | dss_overlay_setup_dispc_manager(mgr); | 
|  | 1535 |  | 
|  | 1536 | omap_dss_add_overlay_manager(mgr); | 
|  | 1537 |  | 
|  | 1538 | r = kobject_init_and_add(&mgr->kobj, &manager_ktype, | 
|  | 1539 | &pdev->dev.kobj, "manager%d", i); | 
|  | 1540 |  | 
|  | 1541 | if (r) { | 
|  | 1542 | DSSERR("failed to create sysfs file\n"); | 
|  | 1543 | continue; | 
|  | 1544 | } | 
|  | 1545 | } | 
|  | 1546 |  | 
|  | 1547 | #ifdef L4_EXAMPLE | 
|  | 1548 | { | 
|  | 1549 | int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) | 
|  | 1550 | { | 
|  | 1551 | DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); | 
|  | 1552 |  | 
|  | 1553 | return 0; | 
|  | 1554 | } | 
|  | 1555 |  | 
|  | 1556 | struct omap_overlay_manager *mgr; | 
|  | 1557 | mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); | 
|  | 1558 |  | 
|  | 1559 | BUG_ON(mgr == NULL); | 
|  | 1560 |  | 
|  | 1561 | mgr->name = "l4"; | 
|  | 1562 | mgr->supported_displays = | 
|  | 1563 | OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; | 
|  | 1564 |  | 
|  | 1565 | mgr->set_device = &omap_dss_set_device; | 
|  | 1566 | mgr->unset_device = &omap_dss_unset_device; | 
|  | 1567 | mgr->apply = &omap_dss_mgr_apply_l4; | 
|  | 1568 | mgr->set_manager_info = &omap_dss_mgr_set_info; | 
|  | 1569 | mgr->get_manager_info = &omap_dss_mgr_get_info; | 
|  | 1570 |  | 
|  | 1571 | dss_overlay_setup_l4_manager(mgr); | 
|  | 1572 |  | 
|  | 1573 | omap_dss_add_overlay_manager(mgr); | 
|  | 1574 |  | 
|  | 1575 | r = kobject_init_and_add(&mgr->kobj, &manager_ktype, | 
|  | 1576 | &pdev->dev.kobj, "managerl4"); | 
|  | 1577 |  | 
|  | 1578 | if (r) | 
|  | 1579 | DSSERR("failed to create sysfs file\n"); | 
|  | 1580 | } | 
|  | 1581 | #endif | 
|  | 1582 |  | 
|  | 1583 | return 0; | 
|  | 1584 | } | 
|  | 1585 |  | 
|  | 1586 | void dss_uninit_overlay_managers(struct platform_device *pdev) | 
|  | 1587 | { | 
|  | 1588 | struct omap_overlay_manager *mgr; | 
|  | 1589 |  | 
|  | 1590 | while (!list_empty(&manager_list)) { | 
|  | 1591 | mgr = list_first_entry(&manager_list, | 
|  | 1592 | struct omap_overlay_manager, list); | 
|  | 1593 | list_del(&mgr->list); | 
|  | 1594 | kobject_del(&mgr->kobj); | 
|  | 1595 | kobject_put(&mgr->kobj); | 
|  | 1596 | kfree(mgr); | 
|  | 1597 | } | 
|  | 1598 |  | 
|  | 1599 | num_managers = 0; | 
|  | 1600 | } | 
|  | 1601 |  | 
|  | 1602 | int omap_dss_get_num_overlay_managers(void) | 
|  | 1603 | { | 
|  | 1604 | return num_managers; | 
|  | 1605 | } | 
|  | 1606 | EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); | 
|  | 1607 |  | 
|  | 1608 | struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) | 
|  | 1609 | { | 
|  | 1610 | int i = 0; | 
|  | 1611 | struct omap_overlay_manager *mgr; | 
|  | 1612 |  | 
|  | 1613 | list_for_each_entry(mgr, &manager_list, list) { | 
|  | 1614 | if (i++ == num) | 
|  | 1615 | return mgr; | 
|  | 1616 | } | 
|  | 1617 |  | 
|  | 1618 | return NULL; | 
|  | 1619 | } | 
|  | 1620 | EXPORT_SYMBOL(omap_dss_get_overlay_manager); | 
|  | 1621 |  |