1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2009 Nokia Corporation
4  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
5  *
6  * Some code and ideas taken from drivers/video/omap/ driver
7  * by Imre Deak.
8  */
9 
10 #define DSS_SUBSYS_NAME "OVERLAY"
11 
12 #include <linux/module.h>
13 #include <linux/err.h>
14 #include <linux/sysfs.h>
15 #include <linux/kobject.h>
16 #include <linux/platform_device.h>
17 
18 #include <video/omapfb_dss.h>
19 
20 #include "dss.h"
21 #include "dss_features.h"
22 
23 static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
24 {
25 	return sysfs_emit(buf, "%s\n", ovl->name);
26 }
27 
28 static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
29 {
30 	return sysfs_emit(buf, "%s\n",
31 			ovl->manager ? ovl->manager->name : "<none>");
32 }
33 
34 static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
35 		size_t size)
36 {
37 	int i, r;
38 	struct omap_overlay_manager *mgr = NULL;
39 	struct omap_overlay_manager *old_mgr;
40 	int len = size;
41 
42 	if (buf[size-1] == '\n')
43 		--len;
44 
45 	if (len > 0) {
46 		for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
47 			mgr = omap_dss_get_overlay_manager(i);
48 
49 			if (sysfs_streq(buf, mgr->name))
50 				break;
51 
52 			mgr = NULL;
53 		}
54 	}
55 
56 	if (len > 0 && mgr == NULL)
57 		return -EINVAL;
58 
59 	if (mgr)
60 		DSSDBG("manager %s found\n", mgr->name);
61 
62 	if (mgr == ovl->manager)
63 		return size;
64 
65 	old_mgr = ovl->manager;
66 
67 	r = dispc_runtime_get();
68 	if (r)
69 		return r;
70 
71 	/* detach old manager */
72 	if (old_mgr) {
73 		r = ovl->unset_manager(ovl);
74 		if (r) {
75 			DSSERR("detach failed\n");
76 			goto err;
77 		}
78 
79 		r = old_mgr->apply(old_mgr);
80 		if (r)
81 			goto err;
82 	}
83 
84 	if (mgr) {
85 		r = ovl->set_manager(ovl, mgr);
86 		if (r) {
87 			DSSERR("Failed to attach overlay\n");
88 			goto err;
89 		}
90 
91 		r = mgr->apply(mgr);
92 		if (r)
93 			goto err;
94 	}
95 
96 	dispc_runtime_put();
97 
98 	return size;
99 
100 err:
101 	dispc_runtime_put();
102 	return r;
103 }
104 
105 static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
106 {
107 	struct omap_overlay_info info;
108 
109 	ovl->get_overlay_info(ovl, &info);
110 
111 	return sysfs_emit(buf, "%d,%d\n",
112 			info.width, info.height);
113 }
114 
115 static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
116 {
117 	struct omap_overlay_info info;
118 
119 	ovl->get_overlay_info(ovl, &info);
120 
121 	return sysfs_emit(buf, "%d\n", info.screen_width);
122 }
123 
124 static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
125 {
126 	struct omap_overlay_info info;
127 
128 	ovl->get_overlay_info(ovl, &info);
129 
130 	return sysfs_emit(buf, "%d,%d\n",
131 			info.pos_x, info.pos_y);
132 }
133 
134 static ssize_t overlay_position_store(struct omap_overlay *ovl,
135 		const char *buf, size_t size)
136 {
137 	int r;
138 	char *last;
139 	struct omap_overlay_info info;
140 
141 	ovl->get_overlay_info(ovl, &info);
142 
143 	info.pos_x = simple_strtoul(buf, &last, 10);
144 	++last;
145 	if (last - buf >= size)
146 		return -EINVAL;
147 
148 	info.pos_y = simple_strtoul(last, &last, 10);
149 
150 	r = ovl->set_overlay_info(ovl, &info);
151 	if (r)
152 		return r;
153 
154 	if (ovl->manager) {
155 		r = ovl->manager->apply(ovl->manager);
156 		if (r)
157 			return r;
158 	}
159 
160 	return size;
161 }
162 
163 static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
164 {
165 	struct omap_overlay_info info;
166 
167 	ovl->get_overlay_info(ovl, &info);
168 
169 	return sysfs_emit(buf, "%d,%d\n",
170 			info.out_width, info.out_height);
171 }
172 
173 static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
174 		const char *buf, size_t size)
175 {
176 	int r;
177 	char *last;
178 	struct omap_overlay_info info;
179 
180 	ovl->get_overlay_info(ovl, &info);
181 
182 	info.out_width = simple_strtoul(buf, &last, 10);
183 	++last;
184 	if (last - buf >= size)
185 		return -EINVAL;
186 
187 	info.out_height = simple_strtoul(last, &last, 10);
188 
189 	r = ovl->set_overlay_info(ovl, &info);
190 	if (r)
191 		return r;
192 
193 	if (ovl->manager) {
194 		r = ovl->manager->apply(ovl->manager);
195 		if (r)
196 			return r;
197 	}
198 
199 	return size;
200 }
201 
202 static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
203 {
204 	return sysfs_emit(buf, "%d\n", ovl->is_enabled(ovl));
205 }
206 
207 static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
208 		size_t size)
209 {
210 	int r;
211 	bool enable;
212 
213 	r = strtobool(buf, &enable);
214 	if (r)
215 		return r;
216 
217 	if (enable)
218 		r = ovl->enable(ovl);
219 	else
220 		r = ovl->disable(ovl);
221 
222 	if (r)
223 		return r;
224 
225 	return size;
226 }
227 
228 static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
229 {
230 	struct omap_overlay_info info;
231 
232 	ovl->get_overlay_info(ovl, &info);
233 
234 	return sysfs_emit(buf, "%d\n",
235 			info.global_alpha);
236 }
237 
238 static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
239 		const char *buf, size_t size)
240 {
241 	int r;
242 	u8 alpha;
243 	struct omap_overlay_info info;
244 
245 	if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
246 		return -ENODEV;
247 
248 	r = kstrtou8(buf, 0, &alpha);
249 	if (r)
250 		return r;
251 
252 	ovl->get_overlay_info(ovl, &info);
253 
254 	info.global_alpha = alpha;
255 
256 	r = ovl->set_overlay_info(ovl, &info);
257 	if (r)
258 		return r;
259 
260 	if (ovl->manager) {
261 		r = ovl->manager->apply(ovl->manager);
262 		if (r)
263 			return r;
264 	}
265 
266 	return size;
267 }
268 
269 static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl,
270 		char *buf)
271 {
272 	struct omap_overlay_info info;
273 
274 	ovl->get_overlay_info(ovl, &info);
275 
276 	return sysfs_emit(buf, "%d\n",
277 			info.pre_mult_alpha);
278 }
279 
280 static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
281 		const char *buf, size_t size)
282 {
283 	int r;
284 	u8 alpha;
285 	struct omap_overlay_info info;
286 
287 	if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
288 		return -ENODEV;
289 
290 	r = kstrtou8(buf, 0, &alpha);
291 	if (r)
292 		return r;
293 
294 	ovl->get_overlay_info(ovl, &info);
295 
296 	info.pre_mult_alpha = alpha;
297 
298 	r = ovl->set_overlay_info(ovl, &info);
299 	if (r)
300 		return r;
301 
302 	if (ovl->manager) {
303 		r = ovl->manager->apply(ovl->manager);
304 		if (r)
305 			return r;
306 	}
307 
308 	return size;
309 }
310 
311 static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
312 {
313 	struct omap_overlay_info info;
314 
315 	ovl->get_overlay_info(ovl, &info);
316 
317 	return sysfs_emit(buf, "%d\n", info.zorder);
318 }
319 
320 static ssize_t overlay_zorder_store(struct omap_overlay *ovl,
321 		const char *buf, size_t size)
322 {
323 	int r;
324 	u8 zorder;
325 	struct omap_overlay_info info;
326 
327 	if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
328 		return -ENODEV;
329 
330 	r = kstrtou8(buf, 0, &zorder);
331 	if (r)
332 		return r;
333 
334 	ovl->get_overlay_info(ovl, &info);
335 
336 	info.zorder = zorder;
337 
338 	r = ovl->set_overlay_info(ovl, &info);
339 	if (r)
340 		return r;
341 
342 	if (ovl->manager) {
343 		r = ovl->manager->apply(ovl->manager);
344 		if (r)
345 			return r;
346 	}
347 
348 	return size;
349 }
350 
351 struct overlay_attribute {
352 	struct attribute attr;
353 	ssize_t (*show)(struct omap_overlay *, char *);
354 	ssize_t	(*store)(struct omap_overlay *, const char *, size_t);
355 };
356 
357 #define OVERLAY_ATTR(_name, _mode, _show, _store) \
358 	struct overlay_attribute overlay_attr_##_name = \
359 	__ATTR(_name, _mode, _show, _store)
360 
361 static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
362 static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
363 		overlay_manager_show, overlay_manager_store);
364 static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
365 static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
366 static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
367 		overlay_position_show, overlay_position_store);
368 static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
369 		overlay_output_size_show, overlay_output_size_store);
370 static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
371 		overlay_enabled_show, overlay_enabled_store);
372 static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
373 		overlay_global_alpha_show, overlay_global_alpha_store);
374 static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
375 		overlay_pre_mult_alpha_show,
376 		overlay_pre_mult_alpha_store);
377 static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
378 		overlay_zorder_show, overlay_zorder_store);
379 
380 static struct attribute *overlay_sysfs_attrs[] = {
381 	&overlay_attr_name.attr,
382 	&overlay_attr_manager.attr,
383 	&overlay_attr_input_size.attr,
384 	&overlay_attr_screen_width.attr,
385 	&overlay_attr_position.attr,
386 	&overlay_attr_output_size.attr,
387 	&overlay_attr_enabled.attr,
388 	&overlay_attr_global_alpha.attr,
389 	&overlay_attr_pre_mult_alpha.attr,
390 	&overlay_attr_zorder.attr,
391 	NULL
392 };
393 ATTRIBUTE_GROUPS(overlay_sysfs);
394 
395 static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
396 		char *buf)
397 {
398 	struct omap_overlay *overlay;
399 	struct overlay_attribute *overlay_attr;
400 
401 	overlay = container_of(kobj, struct omap_overlay, kobj);
402 	overlay_attr = container_of(attr, struct overlay_attribute, attr);
403 
404 	if (!overlay_attr->show)
405 		return -ENOENT;
406 
407 	return overlay_attr->show(overlay, buf);
408 }
409 
410 static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
411 		const char *buf, size_t size)
412 {
413 	struct omap_overlay *overlay;
414 	struct overlay_attribute *overlay_attr;
415 
416 	overlay = container_of(kobj, struct omap_overlay, kobj);
417 	overlay_attr = container_of(attr, struct overlay_attribute, attr);
418 
419 	if (!overlay_attr->store)
420 		return -ENOENT;
421 
422 	return overlay_attr->store(overlay, buf, size);
423 }
424 
425 static const struct sysfs_ops overlay_sysfs_ops = {
426 	.show = overlay_attr_show,
427 	.store = overlay_attr_store,
428 };
429 
430 static struct kobj_type overlay_ktype = {
431 	.sysfs_ops = &overlay_sysfs_ops,
432 	.default_groups = overlay_sysfs_groups,
433 };
434 
435 int dss_overlay_kobj_init(struct omap_overlay *ovl,
436 		struct platform_device *pdev)
437 {
438 	return kobject_init_and_add(&ovl->kobj, &overlay_ktype,
439 			&pdev->dev.kobj, "overlay%d", ovl->id);
440 }
441 
442 void dss_overlay_kobj_uninit(struct omap_overlay *ovl)
443 {
444 	kobject_del(&ovl->kobj);
445 	kobject_put(&ovl->kobj);
446 }
447