diff options
Diffstat (limited to 'drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c')
| -rw-r--r-- | drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c | 605 | 
1 files changed, 605 insertions, 0 deletions
diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c new file mode 100644 index 000000000000..18fa9e1d0033 --- /dev/null +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c @@ -0,0 +1,605 @@ +/* + * linux/drivers/video/omap2/omapfb-sysfs.c + * + * Copyright (C) 2008 Nokia Corporation + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * + * Some code and ideas taken from drivers/video/omap/ driver + * by Imre Deak. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fb.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/omapfb.h> + +#include <video/omapdss.h> +#include <video/omapvrfb.h> + +#include "omapfb.h" + +static ssize_t show_rotate_type(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); + +	return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type); +} + +static ssize_t store_rotate_type(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_mem_region *rg; +	int rot_type; +	int r; + +	r = kstrtoint(buf, 0, &rot_type); +	if (r) +		return r; + +	if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) +		return -EINVAL; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; + +	r = 0; +	if (rot_type == ofbi->rotation_type) +		goto out; + +	rg = omapfb_get_mem_region(ofbi->region); + +	if (rg->size) { +		r = -EBUSY; +		goto put_region; +	} + +	ofbi->rotation_type = rot_type; + +	/* +	 * Since the VRAM for this FB is not allocated at the moment we don't +	 * need to do any further parameter checking at this point. +	 */ +put_region: +	omapfb_put_mem_region(rg); +out: +	unlock_fb_info(fbi); + +	return r ? r : count; +} + + +static ssize_t show_mirror(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); + +	return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror); +} + +static ssize_t store_mirror(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	bool mirror; +	int r; +	struct fb_var_screeninfo new_var; + +	r = strtobool(buf, &mirror); +	if (r) +		return r; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; + +	ofbi->mirror = mirror; + +	omapfb_get_mem_region(ofbi->region); + +	memcpy(&new_var, &fbi->var, sizeof(new_var)); +	r = check_fb_var(fbi, &new_var); +	if (r) +		goto out; +	memcpy(&fbi->var, &new_var, sizeof(fbi->var)); + +	set_fb_fix(fbi); + +	r = omapfb_apply_changes(fbi, 0); +	if (r) +		goto out; + +	r = count; +out: +	omapfb_put_mem_region(ofbi->region); + +	unlock_fb_info(fbi); + +	return r; +} + +static ssize_t show_overlays(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_device *fbdev = ofbi->fbdev; +	ssize_t l = 0; +	int t; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; +	omapfb_lock(fbdev); + +	for (t = 0; t < ofbi->num_overlays; t++) { +		struct omap_overlay *ovl = ofbi->overlays[t]; +		int ovlnum; + +		for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum) +			if (ovl == fbdev->overlays[ovlnum]) +				break; + +		l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", +				t == 0 ? "" : ",", ovlnum); +	} + +	l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + +	omapfb_unlock(fbdev); +	unlock_fb_info(fbi); + +	return l; +} + +static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev, +		struct omap_overlay *ovl) +{ +	int i, t; + +	for (i = 0; i < fbdev->num_fbs; i++) { +		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); + +		for (t = 0; t < ofbi->num_overlays; t++) { +			if (ofbi->overlays[t] == ovl) +				return ofbi; +		} +	} + +	return NULL; +} + +static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_device *fbdev = ofbi->fbdev; +	struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; +	struct omap_overlay *ovl; +	int num_ovls, r, i; +	int len; +	bool added = false; + +	num_ovls = 0; + +	len = strlen(buf); +	if (buf[len - 1] == '\n') +		len = len - 1; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; +	omapfb_lock(fbdev); + +	if (len > 0) { +		char *p = (char *)buf; +		int ovlnum; + +		while (p < buf + len) { +			int found; +			if (num_ovls == OMAPFB_MAX_OVL_PER_FB) { +				r = -EINVAL; +				goto out; +			} + +			ovlnum = simple_strtoul(p, &p, 0); +			if (ovlnum > fbdev->num_overlays) { +				r = -EINVAL; +				goto out; +			} + +			found = 0; +			for (i = 0; i < num_ovls; ++i) { +				if (ovls[i] == fbdev->overlays[ovlnum]) { +					found = 1; +					break; +				} +			} + +			if (!found) +				ovls[num_ovls++] = fbdev->overlays[ovlnum]; + +			p++; +		} +	} + +	for (i = 0; i < num_ovls; ++i) { +		struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]); +		if (ofbi2 && ofbi2 != ofbi) { +			dev_err(fbdev->dev, "overlay already in use\n"); +			r = -EINVAL; +			goto out; +		} +	} + +	/* detach unused overlays */ +	for (i = 0; i < ofbi->num_overlays; ++i) { +		int t, found; + +		ovl = ofbi->overlays[i]; + +		found = 0; + +		for (t = 0; t < num_ovls; ++t) { +			if (ovl == ovls[t]) { +				found = 1; +				break; +			} +		} + +		if (found) +			continue; + +		DBG("detaching %d\n", ofbi->overlays[i]->id); + +		omapfb_get_mem_region(ofbi->region); + +		omapfb_overlay_enable(ovl, 0); + +		if (ovl->manager) +			ovl->manager->apply(ovl->manager); + +		omapfb_put_mem_region(ofbi->region); + +		for (t = i + 1; t < ofbi->num_overlays; t++) { +			ofbi->rotation[t-1] = ofbi->rotation[t]; +			ofbi->overlays[t-1] = ofbi->overlays[t]; +		} + +		ofbi->num_overlays--; +		i--; +	} + +	for (i = 0; i < num_ovls; ++i) { +		int t, found; + +		ovl = ovls[i]; + +		found = 0; + +		for (t = 0; t < ofbi->num_overlays; ++t) { +			if (ovl == ofbi->overlays[t]) { +				found = 1; +				break; +			} +		} + +		if (found) +			continue; +		ofbi->rotation[ofbi->num_overlays] = 0; +		ofbi->overlays[ofbi->num_overlays++] = ovl; + +		added = true; +	} + +	if (added) { +		omapfb_get_mem_region(ofbi->region); + +		r = omapfb_apply_changes(fbi, 0); + +		omapfb_put_mem_region(ofbi->region); + +		if (r) +			goto out; +	} + +	r = count; +out: +	omapfb_unlock(fbdev); +	unlock_fb_info(fbi); + +	return r; +} + +static ssize_t show_overlays_rotate(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	ssize_t l = 0; +	int t; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; + +	for (t = 0; t < ofbi->num_overlays; t++) { +		l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", +				t == 0 ? "" : ",", ofbi->rotation[t]); +	} + +	l += snprintf(buf + l, PAGE_SIZE - l, "\n"); + +	unlock_fb_info(fbi); + +	return l; +} + +static ssize_t store_overlays_rotate(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	int num_ovls = 0, r, i; +	int len; +	bool changed = false; +	u8 rotation[OMAPFB_MAX_OVL_PER_FB]; + +	len = strlen(buf); +	if (buf[len - 1] == '\n') +		len = len - 1; + +	if (!lock_fb_info(fbi)) +		return -ENODEV; + +	if (len > 0) { +		char *p = (char *)buf; + +		while (p < buf + len) { +			int rot; + +			if (num_ovls == ofbi->num_overlays) { +				r = -EINVAL; +				goto out; +			} + +			rot = simple_strtoul(p, &p, 0); +			if (rot < 0 || rot > 3) { +				r = -EINVAL; +				goto out; +			} + +			if (ofbi->rotation[num_ovls] != rot) +				changed = true; + +			rotation[num_ovls++] = rot; + +			p++; +		} +	} + +	if (num_ovls != ofbi->num_overlays) { +		r = -EINVAL; +		goto out; +	} + +	if (changed) { +		for (i = 0; i < num_ovls; ++i) +			ofbi->rotation[i] = rotation[i]; + +		omapfb_get_mem_region(ofbi->region); + +		r = omapfb_apply_changes(fbi, 0); + +		omapfb_put_mem_region(ofbi->region); + +		if (r) +			goto out; + +		/* FIXME error handling? */ +	} + +	r = count; +out: +	unlock_fb_info(fbi); + +	return r; +} + +static ssize_t show_size(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); + +	return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size); +} + +static ssize_t store_size(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); +	struct omapfb2_device *fbdev = ofbi->fbdev; +	struct omap_dss_device *display = fb2display(fbi); +	struct omapfb2_mem_region *rg; +	unsigned long size; +	int r; +	int i; + +	r = kstrtoul(buf, 0, &size); +	if (r) +		return r; + +	size = PAGE_ALIGN(size); + +	if (!lock_fb_info(fbi)) +		return -ENODEV; + +	if (display && display->driver->sync) +		display->driver->sync(display); + +	rg = ofbi->region; + +	down_write_nested(&rg->lock, rg->id); +	atomic_inc(&rg->lock_count); + +	if (atomic_read(&rg->map_count)) { +		r = -EBUSY; +		goto out; +	} + +	for (i = 0; i < fbdev->num_fbs; i++) { +		struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); +		int j; + +		if (ofbi2->region != rg) +			continue; + +		for (j = 0; j < ofbi2->num_overlays; j++) { +			struct omap_overlay *ovl; +			ovl = ofbi2->overlays[j]; +			if (ovl->is_enabled(ovl)) { +				r = -EBUSY; +				goto out; +			} +		} +	} + +	if (size != ofbi->region->size) { +		r = omapfb_realloc_fbmem(fbi, size, ofbi->region->type); +		if (r) { +			dev_err(dev, "realloc fbmem failed\n"); +			goto out; +		} +	} + +	r = count; +out: +	atomic_dec(&rg->lock_count); +	up_write(&rg->lock); + +	unlock_fb_info(fbi); + +	return r; +} + +static ssize_t show_phys(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); + +	return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr); +} + +static ssize_t show_virt(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	struct omapfb_info *ofbi = FB2OFB(fbi); + +	return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr); +} + +static ssize_t show_upd_mode(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	enum omapfb_update_mode mode; +	int r; + +	r = omapfb_get_update_mode(fbi, &mode); + +	if (r) +		return r; + +	return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode); +} + +static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct fb_info *fbi = dev_get_drvdata(dev); +	unsigned mode; +	int r; + +	r = kstrtouint(buf, 0, &mode); +	if (r) +		return r; + +	r = omapfb_set_update_mode(fbi, mode); +	if (r) +		return r; + +	return count; +} + +static struct device_attribute omapfb_attrs[] = { +	__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type, +			store_rotate_type), +	__ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror), +	__ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size), +	__ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays), +	__ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate, +			store_overlays_rotate), +	__ATTR(phys_addr, S_IRUGO, show_phys, NULL), +	__ATTR(virt_addr, S_IRUGO, show_virt, NULL), +	__ATTR(update_mode, S_IRUGO | S_IWUSR, show_upd_mode, store_upd_mode), +}; + +int omapfb_create_sysfs(struct omapfb2_device *fbdev) +{ +	int i; +	int r; + +	DBG("create sysfs for fbs\n"); +	for (i = 0; i < fbdev->num_fbs; i++) { +		int t; +		for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) { +			r = device_create_file(fbdev->fbs[i]->dev, +					&omapfb_attrs[t]); + +			if (r) { +				dev_err(fbdev->dev, "failed to create sysfs " +						"file\n"); +				return r; +			} +		} +	} + +	return 0; +} + +void omapfb_remove_sysfs(struct omapfb2_device *fbdev) +{ +	int i, t; + +	DBG("remove sysfs for fbs\n"); +	for (i = 0; i < fbdev->num_fbs; i++) { +		for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) +			device_remove_file(fbdev->fbs[i]->dev, +					&omapfb_attrs[t]); +	} +} +  | 
