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