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