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