1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * linux/drivers/video/omap2/omapfb-sysfs.c
4 *
5 * Copyright (C) 2008 Nokia Corporation
6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
7 *
8 * Some code and ideas taken from drivers/video/omap/ driver
9 * by Imre Deak.
10 */
11
12#include <linux/fb.h>
13#include <linux/sysfs.h>
14#include <linux/device.h>
15#include <linux/uaccess.h>
16#include <linux/platform_device.h>
17#include <linux/kernel.h>
18#include <linux/mm.h>
19#include <linux/omapfb.h>
20
21#include <video/omapfb_dss.h>
22#include <video/omapvrfb.h>
23
24#include "omapfb.h"
25
26static ssize_t show_rotate_type(struct device *dev,
27		struct device_attribute *attr, char *buf)
28{
29	struct fb_info *fbi = dev_get_drvdata(dev);
30	struct omapfb_info *ofbi = FB2OFB(fbi);
31
32	return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type);
33}
34
35static ssize_t store_rotate_type(struct device *dev,
36		struct device_attribute *attr,
37		const char *buf, size_t count)
38{
39	struct fb_info *fbi = dev_get_drvdata(dev);
40	struct omapfb_info *ofbi = FB2OFB(fbi);
41	struct omapfb2_mem_region *rg;
42	int rot_type;
43	int r;
44
45	r = kstrtoint(buf, 0, &rot_type);
46	if (r)
47		return r;
48
49	if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB)
50		return -EINVAL;
51
52	lock_fb_info(fbi);
53
54	r = 0;
55	if (rot_type == ofbi->rotation_type)
56		goto out;
57
58	rg = omapfb_get_mem_region(ofbi->region);
59
60	if (rg->size) {
61		r = -EBUSY;
62		goto put_region;
63	}
64
65	ofbi->rotation_type = rot_type;
66
67	/*
68	 * Since the VRAM for this FB is not allocated at the moment we don't
69	 * need to do any further parameter checking at this point.
70	 */
71put_region:
72	omapfb_put_mem_region(rg);
73out:
74	unlock_fb_info(fbi);
75
76	return r ? r : count;
77}
78
79
80static ssize_t show_mirror(struct device *dev,
81		struct device_attribute *attr, char *buf)
82{
83	struct fb_info *fbi = dev_get_drvdata(dev);
84	struct omapfb_info *ofbi = FB2OFB(fbi);
85
86	return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror);
87}
88
89static ssize_t store_mirror(struct device *dev,
90		struct device_attribute *attr,
91		const char *buf, size_t count)
92{
93	struct fb_info *fbi = dev_get_drvdata(dev);
94	struct omapfb_info *ofbi = FB2OFB(fbi);
95	bool mirror;
96	int r;
97	struct fb_var_screeninfo new_var;
98
99	r = strtobool(buf, &mirror);
100	if (r)
101		return r;
102
103	lock_fb_info(fbi);
104
105	ofbi->mirror = mirror;
106
107	omapfb_get_mem_region(ofbi->region);
108
109	memcpy(&new_var, &fbi->var, sizeof(new_var));
110	r = check_fb_var(fbi, &new_var);
111	if (r)
112		goto out;
113	memcpy(&fbi->var, &new_var, sizeof(fbi->var));
114
115	set_fb_fix(fbi);
116
117	r = omapfb_apply_changes(fbi, 0);
118	if (r)
119		goto out;
120
121	r = count;
122out:
123	omapfb_put_mem_region(ofbi->region);
124
125	unlock_fb_info(fbi);
126
127	return r;
128}
129
130static ssize_t show_overlays(struct device *dev,
131		struct device_attribute *attr, char *buf)
132{
133	struct fb_info *fbi = dev_get_drvdata(dev);
134	struct omapfb_info *ofbi = FB2OFB(fbi);
135	struct omapfb2_device *fbdev = ofbi->fbdev;
136	ssize_t l = 0;
137	int t;
138
139	lock_fb_info(fbi);
140	omapfb_lock(fbdev);
141
142	for (t = 0; t < ofbi->num_overlays; t++) {
143		struct omap_overlay *ovl = ofbi->overlays[t];
144		int ovlnum;
145
146		for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum)
147			if (ovl == fbdev->overlays[ovlnum])
148				break;
149
150		l += scnprintf(buf + l, PAGE_SIZE - l, "%s%d",
151				t == 0 ? "" : ",", ovlnum);
152	}
153
154	l += scnprintf(buf + l, PAGE_SIZE - l, "\n");
155
156	omapfb_unlock(fbdev);
157	unlock_fb_info(fbi);
158
159	return l;
160}
161
162static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev,
163		struct omap_overlay *ovl)
164{
165	int i, t;
166
167	for (i = 0; i < fbdev->num_fbs; i++) {
168		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
169
170		for (t = 0; t < ofbi->num_overlays; t++) {
171			if (ofbi->overlays[t] == ovl)
172				return ofbi;
173		}
174	}
175
176	return NULL;
177}
178
179static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
180		const char *buf, size_t count)
181{
182	struct fb_info *fbi = dev_get_drvdata(dev);
183	struct omapfb_info *ofbi = FB2OFB(fbi);
184	struct omapfb2_device *fbdev = ofbi->fbdev;
185	struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB];
186	struct omap_overlay *ovl;
187	int num_ovls, r, i;
188	int len;
189	bool added = false;
190
191	num_ovls = 0;
192
193	len = strlen(buf);
194	if (buf[len - 1] == '\n')
195		len = len - 1;
196
197	lock_fb_info(fbi);
198	omapfb_lock(fbdev);
199
200	if (len > 0) {
201		char *p = (char *)buf;
202		int ovlnum;
203
204		while (p < buf + len) {
205			int found;
206			if (num_ovls == OMAPFB_MAX_OVL_PER_FB) {
207				r = -EINVAL;
208				goto out;
209			}
210
211			ovlnum = simple_strtoul(p, &p, 0);
212			if (ovlnum > fbdev->num_overlays) {
213				r = -EINVAL;
214				goto out;
215			}
216
217			found = 0;
218			for (i = 0; i < num_ovls; ++i) {
219				if (ovls[i] == fbdev->overlays[ovlnum]) {
220					found = 1;
221					break;
222				}
223			}
224
225			if (!found)
226				ovls[num_ovls++] = fbdev->overlays[ovlnum];
227
228			p++;
229		}
230	}
231
232	for (i = 0; i < num_ovls; ++i) {
233		struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]);
234		if (ofbi2 && ofbi2 != ofbi) {
235			dev_err(fbdev->dev, "overlay already in use\n");
236			r = -EINVAL;
237			goto out;
238		}
239	}
240
241	/* detach unused overlays */
242	for (i = 0; i < ofbi->num_overlays; ++i) {
243		int t, found;
244
245		ovl = ofbi->overlays[i];
246
247		found = 0;
248
249		for (t = 0; t < num_ovls; ++t) {
250			if (ovl == ovls[t]) {
251				found = 1;
252				break;
253			}
254		}
255
256		if (found)
257			continue;
258
259		DBG("detaching %d\n", ofbi->overlays[i]->id);
260
261		omapfb_get_mem_region(ofbi->region);
262
263		omapfb_overlay_enable(ovl, 0);
264
265		if (ovl->manager)
266			ovl->manager->apply(ovl->manager);
267
268		omapfb_put_mem_region(ofbi->region);
269
270		for (t = i + 1; t < ofbi->num_overlays; t++) {
271			ofbi->rotation[t-1] = ofbi->rotation[t];
272			ofbi->overlays[t-1] = ofbi->overlays[t];
273		}
274
275		ofbi->num_overlays--;
276		i--;
277	}
278
279	for (i = 0; i < num_ovls; ++i) {
280		int t, found;
281
282		ovl = ovls[i];
283
284		found = 0;
285
286		for (t = 0; t < ofbi->num_overlays; ++t) {
287			if (ovl == ofbi->overlays[t]) {
288				found = 1;
289				break;
290			}
291		}
292
293		if (found)
294			continue;
295		ofbi->rotation[ofbi->num_overlays] = 0;
296		ofbi->overlays[ofbi->num_overlays++] = ovl;
297
298		added = true;
299	}
300
301	if (added) {
302		omapfb_get_mem_region(ofbi->region);
303
304		r = omapfb_apply_changes(fbi, 0);
305
306		omapfb_put_mem_region(ofbi->region);
307
308		if (r)
309			goto out;
310	}
311
312	r = count;
313out:
314	omapfb_unlock(fbdev);
315	unlock_fb_info(fbi);
316
317	return r;
318}
319
320static ssize_t show_overlays_rotate(struct device *dev,
321		struct device_attribute *attr, char *buf)
322{
323	struct fb_info *fbi = dev_get_drvdata(dev);
324	struct omapfb_info *ofbi = FB2OFB(fbi);
325	ssize_t l = 0;
326	int t;
327
328	lock_fb_info(fbi);
329
330	for (t = 0; t < ofbi->num_overlays; t++) {
331		l += scnprintf(buf + l, PAGE_SIZE - l, "%s%d",
332				t == 0 ? "" : ",", ofbi->rotation[t]);
333	}
334
335	l += scnprintf(buf + l, PAGE_SIZE - l, "\n");
336
337	unlock_fb_info(fbi);
338
339	return l;
340}
341
342static ssize_t store_overlays_rotate(struct device *dev,
343		struct device_attribute *attr, const char *buf, size_t count)
344{
345	struct fb_info *fbi = dev_get_drvdata(dev);
346	struct omapfb_info *ofbi = FB2OFB(fbi);
347	int num_ovls = 0, r, i;
348	int len;
349	bool changed = false;
350	u8 rotation[OMAPFB_MAX_OVL_PER_FB];
351
352	len = strlen(buf);
353	if (buf[len - 1] == '\n')
354		len = len - 1;
355
356	lock_fb_info(fbi);
357
358	if (len > 0) {
359		char *p = (char *)buf;
360
361		while (p < buf + len) {
362			int rot;
363
364			if (num_ovls == ofbi->num_overlays) {
365				r = -EINVAL;
366				goto out;
367			}
368
369			rot = simple_strtoul(p, &p, 0);
370			if (rot < 0 || rot > 3) {
371				r = -EINVAL;
372				goto out;
373			}
374
375			if (ofbi->rotation[num_ovls] != rot)
376				changed = true;
377
378			rotation[num_ovls++] = rot;
379
380			p++;
381		}
382	}
383
384	if (num_ovls != ofbi->num_overlays) {
385		r = -EINVAL;
386		goto out;
387	}
388
389	if (changed) {
390		for (i = 0; i < num_ovls; ++i)
391			ofbi->rotation[i] = rotation[i];
392
393		omapfb_get_mem_region(ofbi->region);
394
395		r = omapfb_apply_changes(fbi, 0);
396
397		omapfb_put_mem_region(ofbi->region);
398
399		if (r)
400			goto out;
401
402		/* FIXME error handling? */
403	}
404
405	r = count;
406out:
407	unlock_fb_info(fbi);
408
409	return r;
410}
411
412static ssize_t show_size(struct device *dev,
413		struct device_attribute *attr, char *buf)
414{
415	struct fb_info *fbi = dev_get_drvdata(dev);
416	struct omapfb_info *ofbi = FB2OFB(fbi);
417
418	return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region->size);
419}
420
421static ssize_t store_size(struct device *dev, struct device_attribute *attr,
422		const char *buf, size_t count)
423{
424	struct fb_info *fbi = dev_get_drvdata(dev);
425	struct omapfb_info *ofbi = FB2OFB(fbi);
426	struct omapfb2_device *fbdev = ofbi->fbdev;
427	struct omap_dss_device *display = fb2display(fbi);
428	struct omapfb2_mem_region *rg;
429	unsigned long size;
430	int r;
431	int i;
432
433	r = kstrtoul(buf, 0, &size);
434	if (r)
435		return r;
436
437	size = PAGE_ALIGN(size);
438
439	lock_fb_info(fbi);
440
441	if (display && display->driver->sync)
442		display->driver->sync(display);
443
444	rg = ofbi->region;
445
446	down_write_nested(&rg->lock, rg->id);
447	atomic_inc(&rg->lock_count);
448
449	if (atomic_read(&rg->map_count)) {
450		r = -EBUSY;
451		goto out;
452	}
453
454	for (i = 0; i < fbdev->num_fbs; i++) {
455		struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]);
456		int j;
457
458		if (ofbi2->region != rg)
459			continue;
460
461		for (j = 0; j < ofbi2->num_overlays; j++) {
462			struct omap_overlay *ovl;
463			ovl = ofbi2->overlays[j];
464			if (ovl->is_enabled(ovl)) {
465				r = -EBUSY;
466				goto out;
467			}
468		}
469	}
470
471	if (size != ofbi->region->size) {
472		r = omapfb_realloc_fbmem(fbi, size, ofbi->region->type);
473		if (r) {
474			dev_err(dev, "realloc fbmem failed\n");
475			goto out;
476		}
477	}
478
479	r = count;
480out:
481	atomic_dec(&rg->lock_count);
482	up_write(&rg->lock);
483
484	unlock_fb_info(fbi);
485
486	return r;
487}
488
489static ssize_t show_phys(struct device *dev,
490		struct device_attribute *attr, char *buf)
491{
492	struct fb_info *fbi = dev_get_drvdata(dev);
493	struct omapfb_info *ofbi = FB2OFB(fbi);
494
495	return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region->paddr);
496}
497
498static ssize_t show_virt(struct device *dev,
499		struct device_attribute *attr, char *buf)
500{
501	struct fb_info *fbi = dev_get_drvdata(dev);
502	struct omapfb_info *ofbi = FB2OFB(fbi);
503
504	return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);
505}
506
507static ssize_t show_upd_mode(struct device *dev,
508		struct device_attribute *attr, char *buf)
509{
510	struct fb_info *fbi = dev_get_drvdata(dev);
511	enum omapfb_update_mode mode;
512	int r;
513
514	r = omapfb_get_update_mode(fbi, &mode);
515
516	if (r)
517		return r;
518
519	return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode);
520}
521
522static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr,
523		const char *buf, size_t count)
524{
525	struct fb_info *fbi = dev_get_drvdata(dev);
526	unsigned mode;
527	int r;
528
529	r = kstrtouint(buf, 0, &mode);
530	if (r)
531		return r;
532
533	r = omapfb_set_update_mode(fbi, mode);
534	if (r)
535		return r;
536
537	return count;
538}
539
540static struct device_attribute omapfb_attrs[] = {
541	__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
542			store_rotate_type),
543	__ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror),
544	__ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size),
545	__ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays),
546	__ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate,
547			store_overlays_rotate),
548	__ATTR(phys_addr, S_IRUGO, show_phys, NULL),
549	__ATTR(virt_addr, S_IRUGO, show_virt, NULL),
550	__ATTR(update_mode, S_IRUGO | S_IWUSR, show_upd_mode, store_upd_mode),
551};
552
553int omapfb_create_sysfs(struct omapfb2_device *fbdev)
554{
555	int i;
556	int r;
557
558	DBG("create sysfs for fbs\n");
559	for (i = 0; i < fbdev->num_fbs; i++) {
560		int t;
561		for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) {
562			r = device_create_file(fbdev->fbs[i]->dev,
563					&omapfb_attrs[t]);
564
565			if (r) {
566				dev_err(fbdev->dev, "failed to create sysfs "
567						"file\n");
568				return r;
569			}
570		}
571	}
572
573	return 0;
574}
575
576void omapfb_remove_sysfs(struct omapfb2_device *fbdev)
577{
578	int i, t;
579
580	DBG("remove sysfs for fbs\n");
581	for (i = 0; i < fbdev->num_fbs; i++) {
582		for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++)
583			device_remove_file(fbdev->fbs[i]->dev,
584					&omapfb_attrs[t]);
585	}
586}
587
588