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