blob: 567c5599793bc5f5bd5b311760955aa1b6f8d13a [file] [log] [blame]
Greg Hackmannfc29df82013-05-22 14:23:10 -07001/*
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 current_mode_show(struct device *dev,
37 struct device_attribute *attr, char *buf)
38{
39 struct adf_interface *intf = dev_to_adf_interface(dev);
40 struct drm_mode_modeinfo mode;
41
42 adf_interface_current_mode(intf, &mode);
43
44 if (mode.name[0]) {
45 return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name);
46 } else {
47 bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE);
48 return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay,
49 mode.vdisplay, interlaced ? "i" : "");
50 }
51}
52
53static ssize_t type_show(struct device *dev, struct device_attribute *attr,
54 char *buf)
55{
56 struct adf_interface *intf = dev_to_adf_interface(dev);
57 return scnprintf(buf, PAGE_SIZE, "%s\n",
58 adf_interface_type_str(intf));
59}
60
61static ssize_t vsync_timestamp_show(struct device *dev,
62 struct device_attribute *attr, char *buf)
63{
64 struct adf_interface *intf = dev_to_adf_interface(dev);
65 ktime_t timestamp;
66 unsigned long flags;
67
68 read_lock_irqsave(&intf->vsync_lock, flags);
69 memcpy(&timestamp, &intf->vsync_timestamp, sizeof(timestamp));
70 read_unlock_irqrestore(&intf->vsync_lock, flags);
71
72 return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp));
73}
74
75static ssize_t hotplug_detect_show(struct device *dev,
76 struct device_attribute *attr, char *buf)
77{
78 struct adf_interface *intf = dev_to_adf_interface(dev);
79 return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect);
80}
81
82static struct device_attribute adf_interface_attrs[] = {
83 __ATTR_RO(dpms_state),
84 __ATTR_RO(current_mode),
85 __ATTR_RO(hotplug_detect),
86 __ATTR_RO(type),
87 __ATTR_RO(vsync_timestamp),
88};
89
90static char *adf_devnode(struct device *dev, umode_t *mode)
91{
92 return kasprintf(GFP_KERNEL, "adf/%s", dev_name(dev));
93}
94
95int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent)
96{
97 int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL);
98 if (ret < 0) {
99 pr_err("%s: allocating adf minor failed: %d\n", __func__,
100 ret);
101 return ret;
102 }
103
104 obj->minor = ret;
105 obj->dev.parent = parent;
106 obj->dev.class = adf_class;
107 obj->dev.devt = MKDEV(adf_major, obj->minor);
108
109 ret = device_register(&obj->dev);
110 if (ret < 0) {
111 pr_err("%s: registering adf object failed: %d\n", __func__,
112 ret);
113 goto err_device_register;
114 }
115
116 return 0;
117
118err_device_register:
119 idr_remove(&adf_minors, obj->minor);
120 return ret;
121}
122
123static char *adf_device_devnode(struct device *dev, umode_t *mode,
124 kuid_t *uid, kgid_t *gid)
125{
126 struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
127 return kasprintf(GFP_KERNEL, "adf/%s/device", obj->name);
128}
129
130static char *adf_interface_devnode(struct device *dev, umode_t *mode,
131 kuid_t *uid, kgid_t *gid)
132{
133 struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
134 struct adf_interface *intf = adf_obj_to_interface(obj);
135 struct adf_device *parent = adf_interface_parent(intf);
136 return kasprintf(GFP_KERNEL, "adf/%s/interface%d",
137 parent->base.name, intf->base.id);
138}
139
140static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode,
141 kuid_t *uid, kgid_t *gid)
142{
143 struct adf_obj *obj = container_of(dev, struct adf_obj, dev);
144 struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj);
145 struct adf_device *parent = adf_overlay_engine_parent(eng);
146 return kasprintf(GFP_KERNEL, "adf/%s/overlay-engine%d",
147 parent->base.name, eng->base.id);
148}
149
150static void adf_noop_release(struct device *dev)
151{
152}
153
154static struct device_type adf_device_type = {
155 .name = "adf_device",
156 .devnode = adf_device_devnode,
157 .release = adf_noop_release,
158};
159
160static struct device_type adf_interface_type = {
161 .name = "adf_interface",
162 .devnode = adf_interface_devnode,
163 .release = adf_noop_release,
164};
165
166static struct device_type adf_overlay_engine_type = {
167 .name = "adf_overlay_engine",
168 .devnode = adf_overlay_engine_devnode,
169 .release = adf_noop_release,
170};
171
172int adf_device_sysfs_init(struct adf_device *dev)
173{
174 dev->base.dev.type = &adf_device_type;
175 dev_set_name(&dev->base.dev, "%s", dev->base.name);
176 return adf_obj_sysfs_init(&dev->base, dev->dev);
177}
178
179int adf_interface_sysfs_init(struct adf_interface *intf)
180{
181 struct adf_device *parent = adf_interface_parent(intf);
182 size_t i, j;
183 int ret;
184
185 intf->base.dev.type = &adf_interface_type;
186 dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name,
187 intf->base.id);
188
189 ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev);
190 if (ret < 0)
191 return ret;
192
193 for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) {
194 ret = device_create_file(&intf->base.dev,
195 &adf_interface_attrs[i]);
196 if (ret < 0) {
197 dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n",
198 adf_interface_attrs[i].attr.name, ret);
199 goto err;
200 }
201 }
202
203 return 0;
204
205err:
206 for (j = 0; j < i; j++)
207 device_remove_file(&intf->base.dev, &adf_interface_attrs[j]);
208 return ret;
209}
210
211int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng)
212{
213 struct adf_device *parent = adf_overlay_engine_parent(eng);
214
215 eng->base.dev.type = &adf_overlay_engine_type;
216 dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name,
217 eng->base.id);
218
219 return adf_obj_sysfs_init(&eng->base, &parent->base.dev);
220}
221
222struct adf_obj *adf_obj_sysfs_find(int minor)
223{
224 return idr_find(&adf_minors, minor);
225}
226
227void adf_obj_sysfs_destroy(struct adf_obj *obj)
228{
229 idr_remove(&adf_minors, obj->minor);
230 device_unregister(&obj->dev);
231}
232
233void adf_device_sysfs_destroy(struct adf_device *dev)
234{
235 adf_obj_sysfs_destroy(&dev->base);
236}
237
238void adf_interface_sysfs_destroy(struct adf_interface *intf)
239{
240 size_t i;
241
242 for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++)
243 device_remove_file(&intf->base.dev, &adf_interface_attrs[i]);
244 adf_obj_sysfs_destroy(&intf->base);
245}
246
247void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng)
248{
249 adf_obj_sysfs_destroy(&eng->base);
250}
251
252int adf_sysfs_init(void)
253{
254 struct class *class;
255 int ret;
256
257 class = class_create(THIS_MODULE, "adf");
258 if (IS_ERR(class)) {
259 ret = PTR_ERR(class);
260 pr_err("%s: creating class failed: %d\n", __func__, ret);
261 return ret;
262 }
263
264 ret = register_chrdev(0, "adf", &adf_fops);
265 if (ret < 0) {
266 pr_err("%s: registering device failed: %d\n", __func__, ret);
267 goto err_chrdev;
268 }
269
270 class->devnode = adf_devnode;
271 adf_class = class;
272 adf_major = ret;
273 return 0;
274
275err_chrdev:
276 class_destroy(adf_class);
277 return ret;
278}
279
280void adf_sysfs_destroy(void)
281{
282 idr_destroy(&adf_minors);
283 class_destroy(adf_class);
284}