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