1/* 2 * linux/drivers/video/omap2/dss/display.c 3 * 4 * Copyright (C) 2009 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#define DSS_SUBSYS_NAME "DISPLAY" 24 25#include <linux/kernel.h> 26#include <linux/module.h> 27#include <linux/jiffies.h> 28#include <linux/platform_device.h> 29 30#include <video/omapdss.h> 31#include "dss.h" 32#include "dss_features.h" 33 34static ssize_t display_enabled_show(struct device *dev, 35 struct device_attribute *attr, char *buf) 36{ 37 struct omap_dss_device *dssdev = to_dss_device(dev); 38 bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; 39 40 return snprintf(buf, PAGE_SIZE, "%d\n", enabled); 41} 42 43static ssize_t display_enabled_store(struct device *dev, 44 struct device_attribute *attr, 45 const char *buf, size_t size) 46{ 47 struct omap_dss_device *dssdev = to_dss_device(dev); 48 int r; 49 bool enabled; 50 51 r = strtobool(buf, &enabled); 52 if (r) 53 return r; 54 55 if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { 56 if (enabled) { 57 r = dssdev->driver->enable(dssdev); 58 if (r) 59 return r; 60 } else { 61 dssdev->driver->disable(dssdev); 62 } 63 } 64 65 return size; 66} 67 68static ssize_t display_tear_show(struct device *dev, 69 struct device_attribute *attr, char *buf) 70{ 71 struct omap_dss_device *dssdev = to_dss_device(dev); 72 return snprintf(buf, PAGE_SIZE, "%d\n", 73 dssdev->driver->get_te ? 74 dssdev->driver->get_te(dssdev) : 0); 75} 76 77static ssize_t display_tear_store(struct device *dev, 78 struct device_attribute *attr, const char *buf, size_t size) 79{ 80 struct omap_dss_device *dssdev = to_dss_device(dev); 81 int r; 82 bool te; 83 84 if (!dssdev->driver->enable_te || !dssdev->driver->get_te) 85 return -ENOENT; 86 87 r = strtobool(buf, &te); 88 if (r) 89 return r; 90 91 r = dssdev->driver->enable_te(dssdev, te); 92 if (r) 93 return r; 94 95 return size; 96} 97 98static ssize_t display_timings_show(struct device *dev, 99 struct device_attribute *attr, char *buf) 100{ 101 struct omap_dss_device *dssdev = to_dss_device(dev); 102 struct omap_video_timings t; 103 104 if (!dssdev->driver->get_timings) 105 return -ENOENT; 106 107 dssdev->driver->get_timings(dssdev, &t); 108 109 return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", 110 t.pixel_clock, 111 t.x_res, t.hfp, t.hbp, t.hsw, 112 t.y_res, t.vfp, t.vbp, t.vsw); 113} 114 115static ssize_t display_timings_store(struct device *dev, 116 struct device_attribute *attr, const char *buf, size_t size) 117{ 118 struct omap_dss_device *dssdev = to_dss_device(dev); 119 struct omap_video_timings t; 120 int r, found; 121 122 if (!dssdev->driver->set_timings || !dssdev->driver->check_timings) 123 return -ENOENT; 124 125 found = 0; 126#ifdef CONFIG_OMAP2_DSS_VENC 127 if (strncmp("pal", buf, 3) == 0) { 128 t = omap_dss_pal_timings; 129 found = 1; 130 } else if (strncmp("ntsc", buf, 4) == 0) { 131 t = omap_dss_ntsc_timings; 132 found = 1; 133 } 134#endif 135 if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", 136 &t.pixel_clock, 137 &t.x_res, &t.hfp, &t.hbp, &t.hsw, 138 &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) 139 return -EINVAL; 140 141 r = dssdev->driver->check_timings(dssdev, &t); 142 if (r) 143 return r; 144 145 dssdev->driver->set_timings(dssdev, &t); 146 147 return size; 148} 149 150static ssize_t display_rotate_show(struct device *dev, 151 struct device_attribute *attr, char *buf) 152{ 153 struct omap_dss_device *dssdev = to_dss_device(dev); 154 int rotate; 155 if (!dssdev->driver->get_rotate) 156 return -ENOENT; 157 rotate = dssdev->driver->get_rotate(dssdev); 158 return snprintf(buf, PAGE_SIZE, "%u\n", rotate); 159} 160 161static ssize_t display_rotate_store(struct device *dev, 162 struct device_attribute *attr, const char *buf, size_t size) 163{ 164 struct omap_dss_device *dssdev = to_dss_device(dev); 165 int rot, r; 166 167 if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) 168 return -ENOENT; 169 170 r = kstrtoint(buf, 0, &rot); 171 if (r) 172 return r; 173 174 r = dssdev->driver->set_rotate(dssdev, rot); 175 if (r) 176 return r; 177 178 return size; 179} 180 181static ssize_t display_mirror_show(struct device *dev, 182 struct device_attribute *attr, char *buf) 183{ 184 struct omap_dss_device *dssdev = to_dss_device(dev); 185 int mirror; 186 if (!dssdev->driver->get_mirror) 187 return -ENOENT; 188 mirror = dssdev->driver->get_mirror(dssdev); 189 return snprintf(buf, PAGE_SIZE, "%u\n", mirror); 190} 191 192static ssize_t display_mirror_store(struct device *dev, 193 struct device_attribute *attr, const char *buf, size_t size) 194{ 195 struct omap_dss_device *dssdev = to_dss_device(dev); 196 int r; 197 bool mirror; 198 199 if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) 200 return -ENOENT; 201 202 r = strtobool(buf, &mirror); 203 if (r) 204 return r; 205 206 r = dssdev->driver->set_mirror(dssdev, mirror); 207 if (r) 208 return r; 209 210 return size; 211} 212 213static ssize_t display_wss_show(struct device *dev, 214 struct device_attribute *attr, char *buf) 215{ 216 struct omap_dss_device *dssdev = to_dss_device(dev); 217 unsigned int wss; 218 219 if (!dssdev->driver->get_wss) 220 return -ENOENT; 221 222 wss = dssdev->driver->get_wss(dssdev); 223 224 return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); 225} 226 227static ssize_t display_wss_store(struct device *dev, 228 struct device_attribute *attr, const char *buf, size_t size) 229{ 230 struct omap_dss_device *dssdev = to_dss_device(dev); 231 u32 wss; 232 int r; 233 234 if (!dssdev->driver->get_wss || !dssdev->driver->set_wss) 235 return -ENOENT; 236 237 r = kstrtou32(buf, 0, &wss); 238 if (r) 239 return r; 240 241 if (wss > 0xfffff) 242 return -EINVAL; 243 244 r = dssdev->driver->set_wss(dssdev, wss); 245 if (r) 246 return r; 247 248 return size; 249} 250 251static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, 252 display_enabled_show, display_enabled_store); 253static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, 254 display_tear_show, display_tear_store); 255static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, 256 display_timings_show, display_timings_store); 257static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, 258 display_rotate_show, display_rotate_store); 259static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, 260 display_mirror_show, display_mirror_store); 261static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, 262 display_wss_show, display_wss_store); 263 264static struct device_attribute *display_sysfs_attrs[] = { 265 &dev_attr_enabled, 266 &dev_attr_tear_elim, 267 &dev_attr_timings, 268 &dev_attr_rotate, 269 &dev_attr_mirror, 270 &dev_attr_wss, 271 NULL 272}; 273 274void omapdss_default_get_resolution(struct omap_dss_device *dssdev, 275 u16 *xres, u16 *yres) 276{ 277 *xres = dssdev->panel.timings.x_res; 278 *yres = dssdev->panel.timings.y_res; 279} 280EXPORT_SYMBOL(omapdss_default_get_resolution); 281 282int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) 283{ 284 switch (dssdev->type) { 285 case OMAP_DISPLAY_TYPE_DPI: 286 if (dssdev->phy.dpi.data_lines == 24) 287 return 24; 288 else 289 return 16; 290 291 case OMAP_DISPLAY_TYPE_DBI: 292 if (dssdev->ctrl.pixel_size == 24) 293 return 24; 294 else 295 return 16; 296 case OMAP_DISPLAY_TYPE_DSI: 297 if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16) 298 return 24; 299 else 300 return 16; 301 case OMAP_DISPLAY_TYPE_VENC: 302 case OMAP_DISPLAY_TYPE_SDI: 303 case OMAP_DISPLAY_TYPE_HDMI: 304 return 24; 305 default: 306 BUG(); 307 } 308} 309EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); 310 311/* Checks if replication logic should be used. Only use for active matrix, 312 * when overlay is in RGB12U or RGB16 mode, and LCD interface is 313 * 18bpp or 24bpp */ 314bool dss_use_replication(struct omap_dss_device *dssdev, 315 enum omap_color_mode mode) 316{ 317 int bpp; 318 319 if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) 320 return false; 321 322 if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && 323 (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) 324 return false; 325 326 switch (dssdev->type) { 327 case OMAP_DISPLAY_TYPE_DPI: 328 bpp = dssdev->phy.dpi.data_lines; 329 break; 330 case OMAP_DISPLAY_TYPE_HDMI: 331 case OMAP_DISPLAY_TYPE_VENC: 332 case OMAP_DISPLAY_TYPE_SDI: 333 bpp = 24; 334 break; 335 case OMAP_DISPLAY_TYPE_DBI: 336 bpp = dssdev->ctrl.pixel_size; 337 break; 338 case OMAP_DISPLAY_TYPE_DSI: 339 bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); 340 break; 341 default: 342 BUG(); 343 } 344 345 return bpp > 16; 346} 347 348void dss_init_device(struct platform_device *pdev, 349 struct omap_dss_device *dssdev) 350{ 351 struct device_attribute *attr; 352 int i; 353 int r; 354 355 switch (dssdev->type) { 356#ifdef CONFIG_OMAP2_DSS_DPI 357 case OMAP_DISPLAY_TYPE_DPI: 358 r = dpi_init_display(dssdev); 359 break; 360#endif 361#ifdef CONFIG_OMAP2_DSS_RFBI 362 case OMAP_DISPLAY_TYPE_DBI: 363 r = rfbi_init_display(dssdev); 364 break; 365#endif 366#ifdef CONFIG_OMAP2_DSS_VENC 367 case OMAP_DISPLAY_TYPE_VENC: 368 r = venc_init_display(dssdev); 369 break; 370#endif 371#ifdef CONFIG_OMAP2_DSS_SDI 372 case OMAP_DISPLAY_TYPE_SDI: 373 r = sdi_init_display(dssdev); 374 break; 375#endif 376#ifdef CONFIG_OMAP2_DSS_DSI 377 case OMAP_DISPLAY_TYPE_DSI: 378 r = dsi_init_display(dssdev); 379 break; 380#endif 381 case OMAP_DISPLAY_TYPE_HDMI: 382 r = hdmi_init_display(dssdev); 383 break; 384 default: 385 DSSERR("Support for display '%s' not compiled in.\n", 386 dssdev->name); 387 return; 388 } 389 390 if (r) { 391 DSSERR("failed to init display %s\n", dssdev->name); 392 return; 393 } 394 395 /* create device sysfs files */ 396 i = 0; 397 while ((attr = display_sysfs_attrs[i++]) != NULL) { 398 r = device_create_file(&dssdev->dev, attr); 399 if (r) 400 DSSERR("failed to create sysfs file\n"); 401 } 402 403 /* create display? sysfs links */ 404 r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, 405 dev_name(&dssdev->dev)); 406 if (r) 407 DSSERR("failed to create sysfs display link\n"); 408} 409 410void dss_uninit_device(struct platform_device *pdev, 411 struct omap_dss_device *dssdev) 412{ 413 struct device_attribute *attr; 414 int i = 0; 415 416 sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); 417 418 while ((attr = display_sysfs_attrs[i++]) != NULL) 419 device_remove_file(&dssdev->dev, attr); 420 421 if (dssdev->manager) 422 dssdev->manager->unset_device(dssdev->manager); 423} 424 425static int dss_suspend_device(struct device *dev, void *data) 426{ 427 int r; 428 struct omap_dss_device *dssdev = to_dss_device(dev); 429 430 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { 431 dssdev->activate_after_resume = false; 432 return 0; 433 } 434 435 if (!dssdev->driver->suspend) { 436 DSSERR("display '%s' doesn't implement suspend\n", 437 dssdev->name); 438 return -ENOSYS; 439 } 440 441 r = dssdev->driver->suspend(dssdev); 442 if (r) 443 return r; 444 445 dssdev->activate_after_resume = true; 446 447 return 0; 448} 449 450int dss_suspend_all_devices(void) 451{ 452 int r; 453 struct bus_type *bus = dss_get_bus(); 454 455 r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); 456 if (r) { 457 /* resume all displays that were suspended */ 458 dss_resume_all_devices(); 459 return r; 460 } 461 462 return 0; 463} 464 465static int dss_resume_device(struct device *dev, void *data) 466{ 467 int r; 468 struct omap_dss_device *dssdev = to_dss_device(dev); 469 470 if (dssdev->activate_after_resume && dssdev->driver->resume) { 471 r = dssdev->driver->resume(dssdev); 472 if (r) 473 return r; 474 } 475 476 dssdev->activate_after_resume = false; 477 478 return 0; 479} 480 481int dss_resume_all_devices(void) 482{ 483 struct bus_type *bus = dss_get_bus(); 484 485 return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); 486} 487 488static int dss_disable_device(struct device *dev, void *data) 489{ 490 struct omap_dss_device *dssdev = to_dss_device(dev); 491 492 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) 493 dssdev->driver->disable(dssdev); 494 495 return 0; 496} 497 498void dss_disable_all_devices(void) 499{ 500 struct bus_type *bus = dss_get_bus(); 501 bus_for_each_dev(bus, NULL, NULL, dss_disable_device); 502} 503 504 505void omap_dss_get_device(struct omap_dss_device *dssdev) 506{ 507 get_device(&dssdev->dev); 508} 509EXPORT_SYMBOL(omap_dss_get_device); 510 511void omap_dss_put_device(struct omap_dss_device *dssdev) 512{ 513 put_device(&dssdev->dev); 514} 515EXPORT_SYMBOL(omap_dss_put_device); 516 517/* ref count of the found device is incremented. ref count 518 * of from-device is decremented. */ 519struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) 520{ 521 struct device *dev; 522 struct device *dev_start = NULL; 523 struct omap_dss_device *dssdev = NULL; 524 525 int match(struct device *dev, void *data) 526 { 527 return 1; 528 } 529 530 if (from) 531 dev_start = &from->dev; 532 dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); 533 if (dev) 534 dssdev = to_dss_device(dev); 535 if (from) 536 put_device(&from->dev); 537 538 return dssdev; 539} 540EXPORT_SYMBOL(omap_dss_get_next_device); 541 542struct omap_dss_device *omap_dss_find_device(void *data, 543 int (*match)(struct omap_dss_device *dssdev, void *data)) 544{ 545 struct omap_dss_device *dssdev = NULL; 546 547 while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { 548 if (match(dssdev, data)) 549 return dssdev; 550 } 551 552 return NULL; 553} 554EXPORT_SYMBOL(omap_dss_find_device); 555 556int omap_dss_start_device(struct omap_dss_device *dssdev) 557{ 558 if (!dssdev->driver) { 559 DSSDBG("no driver\n"); 560 return -ENODEV; 561 } 562 563 if (!try_module_get(dssdev->dev.driver->owner)) { 564 return -ENODEV; 565 } 566 567 return 0; 568} 569EXPORT_SYMBOL(omap_dss_start_device); 570 571void omap_dss_stop_device(struct omap_dss_device *dssdev) 572{ 573 module_put(dssdev->dev.driver->owner); 574} 575EXPORT_SYMBOL(omap_dss_stop_device); 576 577