diff options
author | Mark Brown <broonie@linaro.org> | 2013-11-06 11:51:38 +0000 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-11-06 11:51:38 +0000 |
commit | 76e8de404a34bfff318da0b31e752db2642511f3 (patch) | |
tree | f486e22499fc5ce7548b76f299ecb91f52baf8ed /drivers/video/adf/adf_sysfs.c | |
parent | e9b3bd248b349433215cf01064a866b632fb3f4b (diff) | |
parent | 354d86f4daecdc3553830e553f6fc17add736234 (diff) |
Merge branch 'linaro-android-3.10-lsk' of git://git.linaro.org/people/jstultz/android into lsk-v3.10-jstultzv3.10/topic/android-fixes
Diffstat (limited to 'drivers/video/adf/adf_sysfs.c')
-rw-r--r-- | drivers/video/adf/adf_sysfs.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/drivers/video/adf/adf_sysfs.c b/drivers/video/adf/adf_sysfs.c new file mode 100644 index 00000000000..076ccbd0cd8 --- /dev/null +++ b/drivers/video/adf/adf_sysfs.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <video/adf_client.h> + +#include "adf.h" +#include "adf_fops.h" +#include "adf_sysfs.h" + +static struct class *adf_class; +static int adf_major; +static DEFINE_IDR(adf_minors); + +#define dev_to_adf_interface(p) \ + adf_obj_to_interface(container_of(p, struct adf_obj, dev)) + +static ssize_t dpms_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + return scnprintf(buf, PAGE_SIZE, "%u\n", + adf_interface_dpms_state(intf)); +} + +static ssize_t dpms_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + u8 dpms_state; + int err; + + err = kstrtou8(buf, 0, &dpms_state); + if (err < 0) + return err; + + err = adf_interface_blank(intf, dpms_state); + if (err < 0) + return err; + + return count; +} + +static ssize_t current_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + struct drm_mode_modeinfo mode; + + adf_interface_current_mode(intf, &mode); + + if (mode.name[0]) { + return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name); + } else { + bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE); + return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay, + mode.vdisplay, interlaced ? "i" : ""); + } +} + +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + return scnprintf(buf, PAGE_SIZE, "%s\n", + adf_interface_type_str(intf)); +} + +static ssize_t vsync_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + ktime_t timestamp; + unsigned long flags; + + read_lock_irqsave(&intf->vsync_lock, flags); + memcpy(×tamp, &intf->vsync_timestamp, sizeof(timestamp)); + read_unlock_irqrestore(&intf->vsync_lock, flags); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp)); +} + +static ssize_t hotplug_detect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adf_interface *intf = dev_to_adf_interface(dev); + return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect); +} + +static struct device_attribute adf_interface_attrs[] = { + __ATTR(dpms_state, S_IRUGO|S_IWUSR, dpms_state_show, dpms_state_store), + __ATTR_RO(current_mode), + __ATTR_RO(hotplug_detect), + __ATTR_RO(type), + __ATTR_RO(vsync_timestamp), +}; + +static char *adf_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "adf/%s", dev_name(dev)); +} + +int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent) +{ + int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL); + if (ret < 0) { + pr_err("%s: allocating adf minor failed: %d\n", __func__, + ret); + return ret; + } + + obj->minor = ret; + obj->dev.parent = parent; + obj->dev.class = adf_class; + obj->dev.devt = MKDEV(adf_major, obj->minor); + + ret = device_register(&obj->dev); + if (ret < 0) { + pr_err("%s: registering adf object failed: %d\n", __func__, + ret); + goto err_device_register; + } + + return 0; + +err_device_register: + idr_remove(&adf_minors, obj->minor); + return ret; +} + +static char *adf_device_devnode(struct device *dev, umode_t *mode, + kuid_t *uid, kgid_t *gid) +{ + struct adf_obj *obj = container_of(dev, struct adf_obj, dev); + return kasprintf(GFP_KERNEL, "adf/%s/device", obj->name); +} + +static char *adf_interface_devnode(struct device *dev, umode_t *mode, + kuid_t *uid, kgid_t *gid) +{ + struct adf_obj *obj = container_of(dev, struct adf_obj, dev); + struct adf_interface *intf = adf_obj_to_interface(obj); + struct adf_device *parent = adf_interface_parent(intf); + return kasprintf(GFP_KERNEL, "adf/%s/interface%d", + parent->base.name, intf->base.id); +} + +static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode, + kuid_t *uid, kgid_t *gid) +{ + struct adf_obj *obj = container_of(dev, struct adf_obj, dev); + struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj); + struct adf_device *parent = adf_overlay_engine_parent(eng); + return kasprintf(GFP_KERNEL, "adf/%s/overlay-engine%d", + parent->base.name, eng->base.id); +} + +static void adf_noop_release(struct device *dev) +{ +} + +static struct device_type adf_device_type = { + .name = "adf_device", + .devnode = adf_device_devnode, + .release = adf_noop_release, +}; + +static struct device_type adf_interface_type = { + .name = "adf_interface", + .devnode = adf_interface_devnode, + .release = adf_noop_release, +}; + +static struct device_type adf_overlay_engine_type = { + .name = "adf_overlay_engine", + .devnode = adf_overlay_engine_devnode, + .release = adf_noop_release, +}; + +int adf_device_sysfs_init(struct adf_device *dev) +{ + dev->base.dev.type = &adf_device_type; + dev_set_name(&dev->base.dev, "%s", dev->base.name); + return adf_obj_sysfs_init(&dev->base, dev->dev); +} + +int adf_interface_sysfs_init(struct adf_interface *intf) +{ + struct adf_device *parent = adf_interface_parent(intf); + size_t i, j; + int ret; + + intf->base.dev.type = &adf_interface_type; + dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name, + intf->base.id); + + ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) { + ret = device_create_file(&intf->base.dev, + &adf_interface_attrs[i]); + if (ret < 0) { + dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n", + adf_interface_attrs[i].attr.name, ret); + goto err; + } + } + + return 0; + +err: + for (j = 0; j < i; j++) + device_remove_file(&intf->base.dev, &adf_interface_attrs[j]); + return ret; +} + +int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng) +{ + struct adf_device *parent = adf_overlay_engine_parent(eng); + + eng->base.dev.type = &adf_overlay_engine_type; + dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name, + eng->base.id); + + return adf_obj_sysfs_init(&eng->base, &parent->base.dev); +} + +struct adf_obj *adf_obj_sysfs_find(int minor) +{ + return idr_find(&adf_minors, minor); +} + +void adf_obj_sysfs_destroy(struct adf_obj *obj) +{ + idr_remove(&adf_minors, obj->minor); + device_unregister(&obj->dev); +} + +void adf_device_sysfs_destroy(struct adf_device *dev) +{ + adf_obj_sysfs_destroy(&dev->base); +} + +void adf_interface_sysfs_destroy(struct adf_interface *intf) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) + device_remove_file(&intf->base.dev, &adf_interface_attrs[i]); + adf_obj_sysfs_destroy(&intf->base); +} + +void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng) +{ + adf_obj_sysfs_destroy(&eng->base); +} + +int adf_sysfs_init(void) +{ + struct class *class; + int ret; + + class = class_create(THIS_MODULE, "adf"); + if (IS_ERR(class)) { + ret = PTR_ERR(class); + pr_err("%s: creating class failed: %d\n", __func__, ret); + return ret; + } + + ret = register_chrdev(0, "adf", &adf_fops); + if (ret < 0) { + pr_err("%s: registering device failed: %d\n", __func__, ret); + goto err_chrdev; + } + + class->devnode = adf_devnode; + adf_class = class; + adf_major = ret; + return 0; + +err_chrdev: + class_destroy(adf_class); + return ret; +} + +void adf_sysfs_destroy(void) +{ + idr_destroy(&adf_minors); + class_destroy(adf_class); +} |