PageRenderTime 55ms CodeModel.GetById 25ms app.highlight 25ms RepoModel.GetById 0ms app.codeStats 1ms

/drivers/video/omap2/dss/overlay.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 764 lines | 569 code | 152 blank | 43 comment | 103 complexity | e292d456590d4b5fc90bfde10ae52569 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * linux/drivers/video/omap2/dss/overlay.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 "OVERLAY"
 24
 25#include <linux/kernel.h>
 26#include <linux/module.h>
 27#include <linux/err.h>
 28#include <linux/sysfs.h>
 29#include <linux/kobject.h>
 30#include <linux/platform_device.h>
 31#include <linux/delay.h>
 32#include <linux/slab.h>
 33
 34#include <video/omapdss.h>
 35#include <plat/cpu.h>
 36
 37#include "dss.h"
 38#include "dss_features.h"
 39
 40static int num_overlays;
 41static struct list_head overlay_list;
 42
 43static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
 44{
 45	return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
 46}
 47
 48static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
 49{
 50	return snprintf(buf, PAGE_SIZE, "%s\n",
 51			ovl->manager ? ovl->manager->name : "<none>");
 52}
 53
 54static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
 55		size_t size)
 56{
 57	int i, r;
 58	struct omap_overlay_manager *mgr = NULL;
 59	struct omap_overlay_manager *old_mgr;
 60	int len = size;
 61
 62	if (buf[size-1] == '\n')
 63		--len;
 64
 65	if (len > 0) {
 66		for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
 67			mgr = omap_dss_get_overlay_manager(i);
 68
 69			if (sysfs_streq(buf, mgr->name))
 70				break;
 71
 72			mgr = NULL;
 73		}
 74	}
 75
 76	if (len > 0 && mgr == NULL)
 77		return -EINVAL;
 78
 79	if (mgr)
 80		DSSDBG("manager %s found\n", mgr->name);
 81
 82	if (mgr == ovl->manager)
 83		return size;
 84
 85	old_mgr = ovl->manager;
 86
 87	r = dispc_runtime_get();
 88	if (r)
 89		return r;
 90
 91	/* detach old manager */
 92	if (old_mgr) {
 93		r = ovl->unset_manager(ovl);
 94		if (r) {
 95			DSSERR("detach failed\n");
 96			goto err;
 97		}
 98
 99		r = old_mgr->apply(old_mgr);
100		if (r)
101			goto err;
102	}
103
104	if (mgr) {
105		r = ovl->set_manager(ovl, mgr);
106		if (r) {
107			DSSERR("Failed to attach overlay\n");
108			goto err;
109		}
110
111		r = mgr->apply(mgr);
112		if (r)
113			goto err;
114	}
115
116	dispc_runtime_put();
117
118	return size;
119
120err:
121	dispc_runtime_put();
122	return r;
123}
124
125static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
126{
127	return snprintf(buf, PAGE_SIZE, "%d,%d\n",
128			ovl->info.width, ovl->info.height);
129}
130
131static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
132{
133	return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width);
134}
135
136static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
137{
138	return snprintf(buf, PAGE_SIZE, "%d,%d\n",
139			ovl->info.pos_x, ovl->info.pos_y);
140}
141
142static ssize_t overlay_position_store(struct omap_overlay *ovl,
143		const char *buf, size_t size)
144{
145	int r;
146	char *last;
147	struct omap_overlay_info info;
148
149	ovl->get_overlay_info(ovl, &info);
150
151	info.pos_x = simple_strtoul(buf, &last, 10);
152	++last;
153	if (last - buf >= size)
154		return -EINVAL;
155
156	info.pos_y = simple_strtoul(last, &last, 10);
157
158	r = ovl->set_overlay_info(ovl, &info);
159	if (r)
160		return r;
161
162	if (ovl->manager) {
163		r = ovl->manager->apply(ovl->manager);
164		if (r)
165			return r;
166	}
167
168	return size;
169}
170
171static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
172{
173	return snprintf(buf, PAGE_SIZE, "%d,%d\n",
174			ovl->info.out_width, ovl->info.out_height);
175}
176
177static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
178		const char *buf, size_t size)
179{
180	int r;
181	char *last;
182	struct omap_overlay_info info;
183
184	ovl->get_overlay_info(ovl, &info);
185
186	info.out_width = simple_strtoul(buf, &last, 10);
187	++last;
188	if (last - buf >= size)
189		return -EINVAL;
190
191	info.out_height = simple_strtoul(last, &last, 10);
192
193	r = ovl->set_overlay_info(ovl, &info);
194	if (r)
195		return r;
196
197	if (ovl->manager) {
198		r = ovl->manager->apply(ovl->manager);
199		if (r)
200			return r;
201	}
202
203	return size;
204}
205
206static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
207{
208	return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled);
209}
210
211static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
212		size_t size)
213{
214	int r, enable;
215	struct omap_overlay_info info;
216
217	ovl->get_overlay_info(ovl, &info);
218
219	r = kstrtoint(buf, 0, &enable);
220	if (r)
221		return r;
222
223	info.enabled = !!enable;
224
225	r = ovl->set_overlay_info(ovl, &info);
226	if (r)
227		return r;
228
229	if (ovl->manager) {
230		r = ovl->manager->apply(ovl->manager);
231		if (r)
232			return r;
233	}
234
235	return size;
236}
237
238static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
239{
240	return snprintf(buf, PAGE_SIZE, "%d\n",
241			ovl->info.global_alpha);
242}
243
244static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
245		const char *buf, size_t size)
246{
247	int r;
248	u8 alpha;
249	struct omap_overlay_info info;
250
251	if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
252		return -ENODEV;
253
254	r = kstrtou8(buf, 0, &alpha);
255	if (r)
256		return r;
257
258	ovl->get_overlay_info(ovl, &info);
259
260	/* Video1 plane does not support global alpha
261	 * to always make it 255 completely opaque
262	 */
263	if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
264			ovl->id == OMAP_DSS_VIDEO1)
265		info.global_alpha = 255;
266	else
267		info.global_alpha = alpha;
268
269	r = ovl->set_overlay_info(ovl, &info);
270	if (r)
271		return r;
272
273	if (ovl->manager) {
274		r = ovl->manager->apply(ovl->manager);
275		if (r)
276			return r;
277	}
278
279	return size;
280}
281
282static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl,
283		char *buf)
284{
285	return snprintf(buf, PAGE_SIZE, "%d\n",
286			ovl->info.pre_mult_alpha);
287}
288
289static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
290		const char *buf, size_t size)
291{
292	int r;
293	u8 alpha;
294	struct omap_overlay_info info;
295
296	r = kstrtou8(buf, 0, &alpha);
297	if (r)
298		return r;
299
300	ovl->get_overlay_info(ovl, &info);
301
302	/* only GFX and Video2 plane support pre alpha multiplied
303	 * set zero for Video1 plane
304	 */
305	if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) &&
306		ovl->id == OMAP_DSS_VIDEO1)
307		info.pre_mult_alpha = 0;
308	else
309		info.pre_mult_alpha = alpha;
310
311	r = ovl->set_overlay_info(ovl, &info);
312	if (r)
313		return r;
314
315	if (ovl->manager) {
316		r = ovl->manager->apply(ovl->manager);
317		if (r)
318			return r;
319	}
320
321	return size;
322}
323
324struct overlay_attribute {
325	struct attribute attr;
326	ssize_t (*show)(struct omap_overlay *, char *);
327	ssize_t	(*store)(struct omap_overlay *, const char *, size_t);
328};
329
330#define OVERLAY_ATTR(_name, _mode, _show, _store) \
331	struct overlay_attribute overlay_attr_##_name = \
332	__ATTR(_name, _mode, _show, _store)
333
334static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
335static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
336		overlay_manager_show, overlay_manager_store);
337static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
338static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
339static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
340		overlay_position_show, overlay_position_store);
341static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
342		overlay_output_size_show, overlay_output_size_store);
343static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
344		overlay_enabled_show, overlay_enabled_store);
345static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
346		overlay_global_alpha_show, overlay_global_alpha_store);
347static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
348		overlay_pre_mult_alpha_show,
349		overlay_pre_mult_alpha_store);
350
351static struct attribute *overlay_sysfs_attrs[] = {
352	&overlay_attr_name.attr,
353	&overlay_attr_manager.attr,
354	&overlay_attr_input_size.attr,
355	&overlay_attr_screen_width.attr,
356	&overlay_attr_position.attr,
357	&overlay_attr_output_size.attr,
358	&overlay_attr_enabled.attr,
359	&overlay_attr_global_alpha.attr,
360	&overlay_attr_pre_mult_alpha.attr,
361	NULL
362};
363
364static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
365		char *buf)
366{
367	struct omap_overlay *overlay;
368	struct overlay_attribute *overlay_attr;
369
370	overlay = container_of(kobj, struct omap_overlay, kobj);
371	overlay_attr = container_of(attr, struct overlay_attribute, attr);
372
373	if (!overlay_attr->show)
374		return -ENOENT;
375
376	return overlay_attr->show(overlay, buf);
377}
378
379static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
380		const char *buf, size_t size)
381{
382	struct omap_overlay *overlay;
383	struct overlay_attribute *overlay_attr;
384
385	overlay = container_of(kobj, struct omap_overlay, kobj);
386	overlay_attr = container_of(attr, struct overlay_attribute, attr);
387
388	if (!overlay_attr->store)
389		return -ENOENT;
390
391	return overlay_attr->store(overlay, buf, size);
392}
393
394static const struct sysfs_ops overlay_sysfs_ops = {
395	.show = overlay_attr_show,
396	.store = overlay_attr_store,
397};
398
399static struct kobj_type overlay_ktype = {
400	.sysfs_ops = &overlay_sysfs_ops,
401	.default_attrs = overlay_sysfs_attrs,
402};
403
404/* Check if overlay parameters are compatible with display */
405int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
406{
407	struct omap_overlay_info *info;
408	u16 outw, outh;
409	u16 dw, dh;
410
411	if (!dssdev)
412		return 0;
413
414	if (!ovl->info.enabled)
415		return 0;
416
417	info = &ovl->info;
418
419	if (info->paddr == 0) {
420		DSSDBG("check_overlay failed: paddr 0\n");
421		return -EINVAL;
422	}
423
424	dssdev->driver->get_resolution(dssdev, &dw, &dh);
425
426	DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
427			ovl->id,
428			info->pos_x, info->pos_y,
429			info->width, info->height,
430			info->out_width, info->out_height,
431			dw, dh);
432
433	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
434		outw = info->width;
435		outh = info->height;
436	} else {
437		if (info->out_width == 0)
438			outw = info->width;
439		else
440			outw = info->out_width;
441
442		if (info->out_height == 0)
443			outh = info->height;
444		else
445			outh = info->out_height;
446	}
447
448	if (dw < info->pos_x + outw) {
449		DSSDBG("check_overlay failed 1: %d < %d + %d\n",
450				dw, info->pos_x, outw);
451		return -EINVAL;
452	}
453
454	if (dh < info->pos_y + outh) {
455		DSSDBG("check_overlay failed 2: %d < %d + %d\n",
456				dh, info->pos_y, outh);
457		return -EINVAL;
458	}
459
460	if ((ovl->supported_modes & info->color_mode) == 0) {
461		DSSERR("overlay doesn't support mode %d\n", info->color_mode);
462		return -EINVAL;
463	}
464
465	return 0;
466}
467
468static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
469		struct omap_overlay_info *info)
470{
471	int r;
472	struct omap_overlay_info old_info;
473
474	old_info = ovl->info;
475	ovl->info = *info;
476
477	if (ovl->manager) {
478		r = dss_check_overlay(ovl, ovl->manager->device);
479		if (r) {
480			ovl->info = old_info;
481			return r;
482		}
483	}
484
485	ovl->info_dirty = true;
486
487	return 0;
488}
489
490static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
491		struct omap_overlay_info *info)
492{
493	*info = ovl->info;
494}
495
496static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
497{
498	return dss_mgr_wait_for_go_ovl(ovl);
499}
500
501static int omap_dss_set_manager(struct omap_overlay *ovl,
502		struct omap_overlay_manager *mgr)
503{
504	if (!mgr)
505		return -EINVAL;
506
507	if (ovl->manager) {
508		DSSERR("overlay '%s' already has a manager '%s'\n",
509				ovl->name, ovl->manager->name);
510		return -EINVAL;
511	}
512
513	if (ovl->info.enabled) {
514		DSSERR("overlay has to be disabled to change the manager\n");
515		return -EINVAL;
516	}
517
518	ovl->manager = mgr;
519
520	/* XXX: When there is an overlay on a DSI manual update display, and
521	 * the overlay is first disabled, then moved to tv, and enabled, we
522	 * seem to get SYNC_LOST_DIGIT error.
523	 *
524	 * Waiting doesn't seem to help, but updating the manual update display
525	 * after disabling the overlay seems to fix this. This hints that the
526	 * overlay is perhaps somehow tied to the LCD output until the output
527	 * is updated.
528	 *
529	 * Userspace workaround for this is to update the LCD after disabling
530	 * the overlay, but before moving the overlay to TV.
531	 */
532	dispc_set_channel_out(ovl->id, mgr->id);
533
534	return 0;
535}
536
537static int omap_dss_unset_manager(struct omap_overlay *ovl)
538{
539	int r;
540
541	if (!ovl->manager) {
542		DSSERR("failed to detach overlay: manager not set\n");
543		return -EINVAL;
544	}
545
546	if (ovl->info.enabled) {
547		DSSERR("overlay has to be disabled to unset the manager\n");
548		return -EINVAL;
549	}
550
551	r = ovl->wait_for_go(ovl);
552	if (r)
553		return r;
554
555	ovl->manager = NULL;
556
557	return 0;
558}
559
560int omap_dss_get_num_overlays(void)
561{
562	return num_overlays;
563}
564EXPORT_SYMBOL(omap_dss_get_num_overlays);
565
566struct omap_overlay *omap_dss_get_overlay(int num)
567{
568	int i = 0;
569	struct omap_overlay *ovl;
570
571	list_for_each_entry(ovl, &overlay_list, list) {
572		if (i++ == num)
573			return ovl;
574	}
575
576	return NULL;
577}
578EXPORT_SYMBOL(omap_dss_get_overlay);
579
580static void omap_dss_add_overlay(struct omap_overlay *overlay)
581{
582	++num_overlays;
583	list_add_tail(&overlay->list, &overlay_list);
584}
585
586static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS];
587
588void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
589{
590	mgr->num_overlays = dss_feat_get_num_ovls();
591	mgr->overlays = dispc_overlays;
592}
593
594#ifdef L4_EXAMPLE
595static struct omap_overlay *l4_overlays[1];
596void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
597{
598	mgr->num_overlays = 1;
599	mgr->overlays = l4_overlays;
600}
601#endif
602
603void dss_init_overlays(struct platform_device *pdev)
604{
605	int i, r;
606
607	INIT_LIST_HEAD(&overlay_list);
608
609	num_overlays = 0;
610
611	for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
612		struct omap_overlay *ovl;
613		ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
614
615		BUG_ON(ovl == NULL);
616
617		switch (i) {
618		case 0:
619			ovl->name = "gfx";
620			ovl->id = OMAP_DSS_GFX;
621			ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
622			ovl->info.global_alpha = 255;
623			break;
624		case 1:
625			ovl->name = "vid1";
626			ovl->id = OMAP_DSS_VIDEO1;
627			ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
628				OMAP_DSS_OVL_CAP_DISPC;
629			ovl->info.global_alpha = 255;
630			break;
631		case 2:
632			ovl->name = "vid2";
633			ovl->id = OMAP_DSS_VIDEO2;
634			ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
635				OMAP_DSS_OVL_CAP_DISPC;
636			ovl->info.global_alpha = 255;
637			break;
638		}
639
640		ovl->set_manager = &omap_dss_set_manager;
641		ovl->unset_manager = &omap_dss_unset_manager;
642		ovl->set_overlay_info = &dss_ovl_set_overlay_info;
643		ovl->get_overlay_info = &dss_ovl_get_overlay_info;
644		ovl->wait_for_go = &dss_ovl_wait_for_go;
645
646		ovl->supported_modes =
647			dss_feat_get_supported_color_modes(ovl->id);
648
649		omap_dss_add_overlay(ovl);
650
651		r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
652				&pdev->dev.kobj, "overlay%d", i);
653
654		if (r) {
655			DSSERR("failed to create sysfs file\n");
656			continue;
657		}
658
659		dispc_overlays[i] = ovl;
660	}
661
662#ifdef L4_EXAMPLE
663	{
664		struct omap_overlay *ovl;
665		ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
666
667		BUG_ON(ovl == NULL);
668
669		ovl->name = "l4";
670		ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
671
672		ovl->set_manager = &omap_dss_set_manager;
673		ovl->unset_manager = &omap_dss_unset_manager;
674		ovl->set_overlay_info = &dss_ovl_set_overlay_info;
675		ovl->get_overlay_info = &dss_ovl_get_overlay_info;
676
677		omap_dss_add_overlay(ovl);
678
679		r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
680				&pdev->dev.kobj, "overlayl4");
681
682		if (r)
683			DSSERR("failed to create sysfs file\n");
684
685		l4_overlays[0] = ovl;
686	}
687#endif
688}
689
690/* connect overlays to the new device, if not already connected. if force
691 * selected, connect always. */
692void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
693{
694	int i;
695	struct omap_overlay_manager *lcd_mgr;
696	struct omap_overlay_manager *tv_mgr;
697	struct omap_overlay_manager *lcd2_mgr = NULL;
698	struct omap_overlay_manager *mgr = NULL;
699
700	lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
701	tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
702	if (dss_has_feature(FEAT_MGR_LCD2))
703		lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2);
704
705	if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
706		if (!lcd2_mgr->device || force) {
707			if (lcd2_mgr->device)
708				lcd2_mgr->unset_device(lcd2_mgr);
709			lcd2_mgr->set_device(lcd2_mgr, dssdev);
710			mgr = lcd2_mgr;
711		}
712	} else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC
713			&& dssdev->type != OMAP_DISPLAY_TYPE_HDMI) {
714		if (!lcd_mgr->device || force) {
715			if (lcd_mgr->device)
716				lcd_mgr->unset_device(lcd_mgr);
717			lcd_mgr->set_device(lcd_mgr, dssdev);
718			mgr = lcd_mgr;
719		}
720	}
721
722	if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
723			|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
724		if (!tv_mgr->device || force) {
725			if (tv_mgr->device)
726				tv_mgr->unset_device(tv_mgr);
727			tv_mgr->set_device(tv_mgr, dssdev);
728			mgr = tv_mgr;
729		}
730	}
731
732	if (mgr) {
733		dispc_runtime_get();
734
735		for (i = 0; i < dss_feat_get_num_ovls(); i++) {
736			struct omap_overlay *ovl;
737			ovl = omap_dss_get_overlay(i);
738			if (!ovl->manager || force) {
739				if (ovl->manager)
740					omap_dss_unset_manager(ovl);
741				omap_dss_set_manager(ovl, mgr);
742			}
743		}
744
745		dispc_runtime_put();
746	}
747}
748
749void dss_uninit_overlays(struct platform_device *pdev)
750{
751	struct omap_overlay *ovl;
752
753	while (!list_empty(&overlay_list)) {
754		ovl = list_first_entry(&overlay_list,
755				struct omap_overlay, list);
756		list_del(&ovl->list);
757		kobject_del(&ovl->kobj);
758		kobject_put(&ovl->kobj);
759		kfree(ovl);
760	}
761
762	num_overlays = 0;
763}
764