1/* 2 * Copyright (C) 2013 Google, Inc. 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15#include <video/adf_client.h> 16 17#include "adf.h" 18#include "adf_fops.h" 19#include "adf_sysfs.h" 20 21static struct class *adf_class; 22static int adf_major; 23static DEFINE_IDR(adf_minors); 24 25#define dev_to_adf_interface(p) \ 26 adf_obj_to_interface(container_of(p, struct adf_obj, dev)) 27 28static ssize_t dpms_state_show(struct device *dev, 29 struct device_attribute *attr, char *buf) 30{ 31 struct adf_interface *intf = dev_to_adf_interface(dev); 32 return scnprintf(buf, PAGE_SIZE, "%u\n", 33 adf_interface_dpms_state(intf)); 34} 35 36static ssize_t dpms_state_store(struct device *dev, 37 struct device_attribute *attr, const char *buf, size_t count) 38{ 39 struct adf_interface *intf = dev_to_adf_interface(dev); 40 u8 dpms_state; 41 int err; 42 43 err = kstrtou8(buf, 0, &dpms_state); 44 if (err < 0) 45 return err; 46 47 err = adf_interface_blank(intf, dpms_state); 48 if (err < 0) 49 return err; 50 51 return count; 52} 53 54static ssize_t current_mode_show(struct device *dev, 55 struct device_attribute *attr, char *buf) 56{ 57 struct adf_interface *intf = dev_to_adf_interface(dev); 58 struct drm_mode_modeinfo mode; 59 60 adf_interface_current_mode(intf, &mode); 61 62 if (mode.name[0]) { 63 return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name); 64 } else { 65 bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE); 66 return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay, 67 mode.vdisplay, interlaced ? "i" : ""); 68 } 69} 70 71static ssize_t type_show(struct device *dev, struct device_attribute *attr, 72 char *buf) 73{ 74 struct adf_interface *intf = dev_to_adf_interface(dev); 75 return scnprintf(buf, PAGE_SIZE, "%s\n", 76 adf_interface_type_str(intf)); 77} 78 79static ssize_t vsync_timestamp_show(struct device *dev, 80 struct device_attribute *attr, char *buf) 81{ 82 struct adf_interface *intf = dev_to_adf_interface(dev); 83 ktime_t timestamp; 84 unsigned long flags; 85 86 read_lock_irqsave(&intf->vsync_lock, flags); 87 memcpy(×tamp, &intf->vsync_timestamp, sizeof(timestamp)); 88 read_unlock_irqrestore(&intf->vsync_lock, flags); 89 90 return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp)); 91} 92 93static ssize_t hotplug_detect_show(struct device *dev, 94 struct device_attribute *attr, char *buf) 95{ 96 struct adf_interface *intf = dev_to_adf_interface(dev); 97 return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect); 98} 99 100static struct device_attribute adf_interface_attrs[] = { 101 __ATTR(dpms_state, S_IRUGO|S_IWUSR, dpms_state_show, dpms_state_store), 102 __ATTR_RO(current_mode), 103 __ATTR_RO(hotplug_detect), 104 __ATTR_RO(type), 105 __ATTR_RO(vsync_timestamp), 106}; 107 108int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent) 109{ 110 int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL); 111 if (ret < 0) { 112 pr_err("%s: allocating adf minor failed: %d\n", __func__, 113 ret); 114 return ret; 115 } 116 117 obj->minor = ret; 118 obj->dev.parent = parent; 119 obj->dev.class = adf_class; 120 obj->dev.devt = MKDEV(adf_major, obj->minor); 121 122 ret = device_register(&obj->dev); 123 if (ret < 0) { 124 pr_err("%s: registering adf object failed: %d\n", __func__, 125 ret); 126 goto err_device_register; 127 } 128 129 return 0; 130 131err_device_register: 132 idr_remove(&adf_minors, obj->minor); 133 return ret; 134} 135 136static char *adf_device_devnode(struct device *dev, umode_t *mode, 137 kuid_t *uid, kgid_t *gid) 138{ 139 struct adf_obj *obj = container_of(dev, struct adf_obj, dev); 140 return kasprintf(GFP_KERNEL, "adf%d", obj->id); 141} 142 143static char *adf_interface_devnode(struct device *dev, umode_t *mode, 144 kuid_t *uid, kgid_t *gid) 145{ 146 struct adf_obj *obj = container_of(dev, struct adf_obj, dev); 147 struct adf_interface *intf = adf_obj_to_interface(obj); 148 struct adf_device *parent = adf_interface_parent(intf); 149 return kasprintf(GFP_KERNEL, "adf-interface%d.%d", 150 parent->base.id, intf->base.id); 151} 152 153static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode, 154 kuid_t *uid, kgid_t *gid) 155{ 156 struct adf_obj *obj = container_of(dev, struct adf_obj, dev); 157 struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj); 158 struct adf_device *parent = adf_overlay_engine_parent(eng); 159 return kasprintf(GFP_KERNEL, "adf-overlay-engine%d.%d", 160 parent->base.id, eng->base.id); 161} 162 163static void adf_noop_release(struct device *dev) 164{ 165} 166 167static struct device_type adf_device_type = { 168 .name = "adf_device", 169 .devnode = adf_device_devnode, 170 .release = adf_noop_release, 171}; 172 173static struct device_type adf_interface_type = { 174 .name = "adf_interface", 175 .devnode = adf_interface_devnode, 176 .release = adf_noop_release, 177}; 178 179static struct device_type adf_overlay_engine_type = { 180 .name = "adf_overlay_engine", 181 .devnode = adf_overlay_engine_devnode, 182 .release = adf_noop_release, 183}; 184 185int adf_device_sysfs_init(struct adf_device *dev) 186{ 187 dev->base.dev.type = &adf_device_type; 188 dev_set_name(&dev->base.dev, "%s", dev->base.name); 189 return adf_obj_sysfs_init(&dev->base, dev->dev); 190} 191 192int adf_interface_sysfs_init(struct adf_interface *intf) 193{ 194 struct adf_device *parent = adf_interface_parent(intf); 195 size_t i, j; 196 int ret; 197 198 intf->base.dev.type = &adf_interface_type; 199 dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name, 200 intf->base.id); 201 202 ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev); 203 if (ret < 0) 204 return ret; 205 206 for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) { 207 ret = device_create_file(&intf->base.dev, 208 &adf_interface_attrs[i]); 209 if (ret < 0) { 210 dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n", 211 adf_interface_attrs[i].attr.name, ret); 212 goto err; 213 } 214 } 215 216 return 0; 217 218err: 219 for (j = 0; j < i; j++) 220 device_remove_file(&intf->base.dev, &adf_interface_attrs[j]); 221 return ret; 222} 223 224int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng) 225{ 226 struct adf_device *parent = adf_overlay_engine_parent(eng); 227 228 eng->base.dev.type = &adf_overlay_engine_type; 229 dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name, 230 eng->base.id); 231 232 return adf_obj_sysfs_init(&eng->base, &parent->base.dev); 233} 234 235struct adf_obj *adf_obj_sysfs_find(int minor) 236{ 237 return idr_find(&adf_minors, minor); 238} 239 240void adf_obj_sysfs_destroy(struct adf_obj *obj) 241{ 242 idr_remove(&adf_minors, obj->minor); 243 device_unregister(&obj->dev); 244} 245 246void adf_device_sysfs_destroy(struct adf_device *dev) 247{ 248 adf_obj_sysfs_destroy(&dev->base); 249} 250 251void adf_interface_sysfs_destroy(struct adf_interface *intf) 252{ 253 size_t i; 254 255 for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) 256 device_remove_file(&intf->base.dev, &adf_interface_attrs[i]); 257 adf_obj_sysfs_destroy(&intf->base); 258} 259 260void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng) 261{ 262 adf_obj_sysfs_destroy(&eng->base); 263} 264 265int adf_sysfs_init(void) 266{ 267 struct class *class; 268 int ret; 269 270 class = class_create(THIS_MODULE, "adf"); 271 if (IS_ERR(class)) { 272 ret = PTR_ERR(class); 273 pr_err("%s: creating class failed: %d\n", __func__, ret); 274 return ret; 275 } 276 277 ret = register_chrdev(0, "adf", &adf_fops); 278 if (ret < 0) { 279 pr_err("%s: registering device failed: %d\n", __func__, ret); 280 goto err_chrdev; 281 } 282 283 adf_class = class; 284 adf_major = ret; 285 return 0; 286 287err_chrdev: 288 class_destroy(adf_class); 289 return ret; 290} 291 292void adf_sysfs_destroy(void) 293{ 294 idr_destroy(&adf_minors); 295 class_destroy(adf_class); 296} 297