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/omapdss.h>
30
31#include "dss.h"
32#include "dss_features.h"
33
34static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
35{
36	return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
37}
38
39static 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
45static 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
111err:
112	dispc_runtime_put();
113	return r;
114}
115
116static 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
126static 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
135static 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
145static 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
174static 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
184static 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
213static 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
218static 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
239static 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
249static 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
280static 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
291static 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
322static 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
331static 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
362struct 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
372static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
373static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
374		overlay_manager_show, overlay_manager_store);
375static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
376static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
377static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
378		overlay_position_show, overlay_position_store);
379static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
380		overlay_output_size_show, overlay_output_size_store);
381static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
382		overlay_enabled_show, overlay_enabled_store);
383static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
384		overlay_global_alpha_show, overlay_global_alpha_store);
385static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
386		overlay_pre_mult_alpha_show,
387		overlay_pre_mult_alpha_store);
388static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
389		overlay_zorder_show, overlay_zorder_store);
390
391static 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
405static 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
420static 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
435static const struct sysfs_ops overlay_sysfs_ops = {
436	.show = overlay_attr_show,
437	.store = overlay_attr_store,
438};
439
440static struct kobj_type overlay_ktype = {
441	.sysfs_ops = &overlay_sysfs_ops,
442	.default_attrs = overlay_sysfs_attrs,
443};
444
445int 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
452void dss_overlay_kobj_uninit(struct omap_overlay *ovl)
453{
454	kobject_del(&ovl->kobj);
455	kobject_put(&ovl->kobj);
456}
457