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