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(&timestamp, &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