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 "MANAGER"
22 
23 #include <linux/kernel.h>
24 #include <linux/slab.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/jiffies.h>
28 
29 #include <video/omapfb_dss.h>
30 
31 #include "dss.h"
32 #include "dss_features.h"
33 
34 static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
35 {
36 	return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
37 }
38 
39 static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
40 {
41 	struct omap_dss_device *dssdev = mgr->get_device(mgr);
42 
43 	return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ?
44 			dssdev->name : "<none>");
45 }
46 
47 static int manager_display_match(struct omap_dss_device *dssdev, void *data)
48 {
49 	const char *str = data;
50 
51 	return sysfs_streq(dssdev->name, str);
52 }
53 
54 static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
55 		const char *buf, size_t size)
56 {
57 	int r = 0;
58 	size_t len = size;
59 	struct omap_dss_device *dssdev = NULL;
60 	struct omap_dss_device *old_dssdev;
61 
62 	if (buf[size-1] == '\n')
63 		--len;
64 
65 	if (len > 0)
66 		dssdev = omap_dss_find_device((void *)buf,
67 			manager_display_match);
68 
69 	if (len > 0 && dssdev == NULL)
70 		return -EINVAL;
71 
72 	if (dssdev) {
73 		DSSDBG("display %s found\n", dssdev->name);
74 
75 		if (omapdss_device_is_connected(dssdev)) {
76 			DSSERR("new display is already connected\n");
77 			r = -EINVAL;
78 			goto put_device;
79 		}
80 
81 		if (omapdss_device_is_enabled(dssdev)) {
82 			DSSERR("new display is not disabled\n");
83 			r = -EINVAL;
84 			goto put_device;
85 		}
86 	}
87 
88 	old_dssdev = mgr->get_device(mgr);
89 	if (old_dssdev) {
90 		if (omapdss_device_is_enabled(old_dssdev)) {
91 			DSSERR("old display is not disabled\n");
92 			r = -EINVAL;
93 			goto put_device;
94 		}
95 
96 		old_dssdev->driver->disconnect(old_dssdev);
97 	}
98 
99 	if (dssdev) {
100 		r = dssdev->driver->connect(dssdev);
101 		if (r) {
102 			DSSERR("failed to connect new device\n");
103 			goto put_device;
104 		}
105 
106 		old_dssdev = mgr->get_device(mgr);
107 		if (old_dssdev != dssdev) {
108 			DSSERR("failed to connect device to this manager\n");
109 			dssdev->driver->disconnect(dssdev);
110 			goto put_device;
111 		}
112 
113 		r = mgr->apply(mgr);
114 		if (r) {
115 			DSSERR("failed to apply dispc config\n");
116 			goto put_device;
117 		}
118 	}
119 
120 put_device:
121 	if (dssdev)
122 		omap_dss_put_device(dssdev);
123 
124 	return r ? r : size;
125 }
126 
127 static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
128 					  char *buf)
129 {
130 	struct omap_overlay_manager_info info;
131 
132 	mgr->get_manager_info(mgr, &info);
133 
134 	return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color);
135 }
136 
137 static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
138 					   const char *buf, size_t size)
139 {
140 	struct omap_overlay_manager_info info;
141 	u32 color;
142 	int r;
143 
144 	r = kstrtouint(buf, 0, &color);
145 	if (r)
146 		return r;
147 
148 	mgr->get_manager_info(mgr, &info);
149 
150 	info.default_color = color;
151 
152 	r = mgr->set_manager_info(mgr, &info);
153 	if (r)
154 		return r;
155 
156 	r = mgr->apply(mgr);
157 	if (r)
158 		return r;
159 
160 	return size;
161 }
162 
163 static const char *trans_key_type_str[] = {
164 	"gfx-destination",
165 	"video-source",
166 };
167 
168 static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
169 					   char *buf)
170 {
171 	enum omap_dss_trans_key_type key_type;
172 	struct omap_overlay_manager_info info;
173 
174 	mgr->get_manager_info(mgr, &info);
175 
176 	key_type = info.trans_key_type;
177 	BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
178 
179 	return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
180 }
181 
182 static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
183 					    const char *buf, size_t size)
184 {
185 	enum omap_dss_trans_key_type key_type;
186 	struct omap_overlay_manager_info info;
187 	int r;
188 
189 	for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
190 			key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
191 		if (sysfs_streq(buf, trans_key_type_str[key_type]))
192 			break;
193 	}
194 
195 	if (key_type == ARRAY_SIZE(trans_key_type_str))
196 		return -EINVAL;
197 
198 	mgr->get_manager_info(mgr, &info);
199 
200 	info.trans_key_type = key_type;
201 
202 	r = mgr->set_manager_info(mgr, &info);
203 	if (r)
204 		return r;
205 
206 	r = mgr->apply(mgr);
207 	if (r)
208 		return r;
209 
210 	return size;
211 }
212 
213 static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
214 					    char *buf)
215 {
216 	struct omap_overlay_manager_info info;
217 
218 	mgr->get_manager_info(mgr, &info);
219 
220 	return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key);
221 }
222 
223 static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
224 					     const char *buf, size_t size)
225 {
226 	struct omap_overlay_manager_info info;
227 	u32 key_value;
228 	int r;
229 
230 	r = kstrtouint(buf, 0, &key_value);
231 	if (r)
232 		return r;
233 
234 	mgr->get_manager_info(mgr, &info);
235 
236 	info.trans_key = key_value;
237 
238 	r = mgr->set_manager_info(mgr, &info);
239 	if (r)
240 		return r;
241 
242 	r = mgr->apply(mgr);
243 	if (r)
244 		return r;
245 
246 	return size;
247 }
248 
249 static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
250 					      char *buf)
251 {
252 	struct omap_overlay_manager_info info;
253 
254 	mgr->get_manager_info(mgr, &info);
255 
256 	return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled);
257 }
258 
259 static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
260 					       const char *buf, size_t size)
261 {
262 	struct omap_overlay_manager_info info;
263 	bool enable;
264 	int r;
265 
266 	r = strtobool(buf, &enable);
267 	if (r)
268 		return r;
269 
270 	mgr->get_manager_info(mgr, &info);
271 
272 	info.trans_enabled = enable;
273 
274 	r = mgr->set_manager_info(mgr, &info);
275 	if (r)
276 		return r;
277 
278 	r = mgr->apply(mgr);
279 	if (r)
280 		return r;
281 
282 	return size;
283 }
284 
285 static ssize_t manager_alpha_blending_enabled_show(
286 		struct omap_overlay_manager *mgr, char *buf)
287 {
288 	struct omap_overlay_manager_info info;
289 
290 	if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
291 		return -ENODEV;
292 
293 	mgr->get_manager_info(mgr, &info);
294 
295 	return snprintf(buf, PAGE_SIZE, "%d\n",
296 		info.partial_alpha_enabled);
297 }
298 
299 static ssize_t manager_alpha_blending_enabled_store(
300 		struct omap_overlay_manager *mgr,
301 		const char *buf, size_t size)
302 {
303 	struct omap_overlay_manager_info info;
304 	bool enable;
305 	int r;
306 
307 	if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
308 		return -ENODEV;
309 
310 	r = strtobool(buf, &enable);
311 	if (r)
312 		return r;
313 
314 	mgr->get_manager_info(mgr, &info);
315 
316 	info.partial_alpha_enabled = enable;
317 
318 	r = mgr->set_manager_info(mgr, &info);
319 	if (r)
320 		return r;
321 
322 	r = mgr->apply(mgr);
323 	if (r)
324 		return r;
325 
326 	return size;
327 }
328 
329 static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
330 		char *buf)
331 {
332 	struct omap_overlay_manager_info info;
333 
334 	mgr->get_manager_info(mgr, &info);
335 
336 	return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable);
337 }
338 
339 static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
340 		const char *buf, size_t size)
341 {
342 	struct omap_overlay_manager_info info;
343 	int r;
344 	bool enable;
345 
346 	if (!dss_has_feature(FEAT_CPR))
347 		return -ENODEV;
348 
349 	r = strtobool(buf, &enable);
350 	if (r)
351 		return r;
352 
353 	mgr->get_manager_info(mgr, &info);
354 
355 	if (info.cpr_enable == enable)
356 		return size;
357 
358 	info.cpr_enable = enable;
359 
360 	r = mgr->set_manager_info(mgr, &info);
361 	if (r)
362 		return r;
363 
364 	r = mgr->apply(mgr);
365 	if (r)
366 		return r;
367 
368 	return size;
369 }
370 
371 static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
372 		char *buf)
373 {
374 	struct omap_overlay_manager_info info;
375 
376 	mgr->get_manager_info(mgr, &info);
377 
378 	return snprintf(buf, PAGE_SIZE,
379 			"%d %d %d %d %d %d %d %d %d\n",
380 			info.cpr_coefs.rr,
381 			info.cpr_coefs.rg,
382 			info.cpr_coefs.rb,
383 			info.cpr_coefs.gr,
384 			info.cpr_coefs.gg,
385 			info.cpr_coefs.gb,
386 			info.cpr_coefs.br,
387 			info.cpr_coefs.bg,
388 			info.cpr_coefs.bb);
389 }
390 
391 static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
392 		const char *buf, size_t size)
393 {
394 	struct omap_overlay_manager_info info;
395 	struct omap_dss_cpr_coefs coefs;
396 	int r, i;
397 	s16 *arr;
398 
399 	if (!dss_has_feature(FEAT_CPR))
400 		return -ENODEV;
401 
402 	if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
403 				&coefs.rr, &coefs.rg, &coefs.rb,
404 				&coefs.gr, &coefs.gg, &coefs.gb,
405 				&coefs.br, &coefs.bg, &coefs.bb) != 9)
406 		return -EINVAL;
407 
408 	arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
409 		coefs.gr, coefs.gg, coefs.gb,
410 		coefs.br, coefs.bg, coefs.bb };
411 
412 	for (i = 0; i < 9; ++i) {
413 		if (arr[i] < -512 || arr[i] > 511)
414 			return -EINVAL;
415 	}
416 
417 	mgr->get_manager_info(mgr, &info);
418 
419 	info.cpr_coefs = coefs;
420 
421 	r = mgr->set_manager_info(mgr, &info);
422 	if (r)
423 		return r;
424 
425 	r = mgr->apply(mgr);
426 	if (r)
427 		return r;
428 
429 	return size;
430 }
431 
432 struct manager_attribute {
433 	struct attribute attr;
434 	ssize_t (*show)(struct omap_overlay_manager *, char *);
435 	ssize_t	(*store)(struct omap_overlay_manager *, const char *, size_t);
436 };
437 
438 #define MANAGER_ATTR(_name, _mode, _show, _store) \
439 	struct manager_attribute manager_attr_##_name = \
440 	__ATTR(_name, _mode, _show, _store)
441 
442 static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
443 static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
444 		manager_display_show, manager_display_store);
445 static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
446 		manager_default_color_show, manager_default_color_store);
447 static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
448 		manager_trans_key_type_show, manager_trans_key_type_store);
449 static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
450 		manager_trans_key_value_show, manager_trans_key_value_store);
451 static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
452 		manager_trans_key_enabled_show,
453 		manager_trans_key_enabled_store);
454 static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
455 		manager_alpha_blending_enabled_show,
456 		manager_alpha_blending_enabled_store);
457 static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
458 		manager_cpr_enable_show,
459 		manager_cpr_enable_store);
460 static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
461 		manager_cpr_coef_show,
462 		manager_cpr_coef_store);
463 
464 
465 static struct attribute *manager_sysfs_attrs[] = {
466 	&manager_attr_name.attr,
467 	&manager_attr_display.attr,
468 	&manager_attr_default_color.attr,
469 	&manager_attr_trans_key_type.attr,
470 	&manager_attr_trans_key_value.attr,
471 	&manager_attr_trans_key_enabled.attr,
472 	&manager_attr_alpha_blending_enabled.attr,
473 	&manager_attr_cpr_enable.attr,
474 	&manager_attr_cpr_coef.attr,
475 	NULL
476 };
477 
478 static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
479 		char *buf)
480 {
481 	struct omap_overlay_manager *manager;
482 	struct manager_attribute *manager_attr;
483 
484 	manager = container_of(kobj, struct omap_overlay_manager, kobj);
485 	manager_attr = container_of(attr, struct manager_attribute, attr);
486 
487 	if (!manager_attr->show)
488 		return -ENOENT;
489 
490 	return manager_attr->show(manager, buf);
491 }
492 
493 static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
494 		const char *buf, size_t size)
495 {
496 	struct omap_overlay_manager *manager;
497 	struct manager_attribute *manager_attr;
498 
499 	manager = container_of(kobj, struct omap_overlay_manager, kobj);
500 	manager_attr = container_of(attr, struct manager_attribute, attr);
501 
502 	if (!manager_attr->store)
503 		return -ENOENT;
504 
505 	return manager_attr->store(manager, buf, size);
506 }
507 
508 static const struct sysfs_ops manager_sysfs_ops = {
509 	.show = manager_attr_show,
510 	.store = manager_attr_store,
511 };
512 
513 static struct kobj_type manager_ktype = {
514 	.sysfs_ops = &manager_sysfs_ops,
515 	.default_attrs = manager_sysfs_attrs,
516 };
517 
518 int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
519 		struct platform_device *pdev)
520 {
521 	return kobject_init_and_add(&mgr->kobj, &manager_ktype,
522 			&pdev->dev.kobj, "manager%d", mgr->id);
523 }
524 
525 void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr)
526 {
527 	kobject_del(&mgr->kobj);
528 	kobject_put(&mgr->kobj);
529 
530 	memset(&mgr->kobj, 0, sizeof(mgr->kobj));
531 }
532