1 /* 2 * Copyright (C) 2014 Traphandler 3 * Copyright (C) 2014 Free Electrons 4 * Copyright (C) 2014 Atmel 5 * 6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published by 11 * the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include <linux/clk.h> 23 #include <linux/irq.h> 24 #include <linux/irqchip.h> 25 #include <linux/module.h> 26 #include <linux/pm_runtime.h> 27 28 #include "atmel_hlcdc_dc.h" 29 30 #define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8 31 32 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { 33 { 34 .name = "base", 35 .formats = &atmel_hlcdc_plane_rgb_formats, 36 .regs_offset = 0x40, 37 .id = 0, 38 .type = ATMEL_HLCDC_BASE_LAYER, 39 .nconfigs = 7, 40 .layout = { 41 .xstride = { 2 }, 42 .default_color = 3, 43 .general_config = 4, 44 .disc_pos = 5, 45 .disc_size = 6, 46 }, 47 }, 48 { 49 .name = "overlay1", 50 .formats = &atmel_hlcdc_plane_rgb_formats, 51 .regs_offset = 0x140, 52 .id = 1, 53 .type = ATMEL_HLCDC_OVERLAY_LAYER, 54 .nconfigs = 10, 55 .layout = { 56 .pos = 2, 57 .size = 3, 58 .xstride = { 4 }, 59 .pstride = { 5 }, 60 .default_color = 6, 61 .chroma_key = 7, 62 .chroma_key_mask = 8, 63 .general_config = 9, 64 }, 65 }, 66 { 67 .name = "overlay2", 68 .formats = &atmel_hlcdc_plane_rgb_formats, 69 .regs_offset = 0x240, 70 .id = 2, 71 .type = ATMEL_HLCDC_OVERLAY_LAYER, 72 .nconfigs = 10, 73 .layout = { 74 .pos = 2, 75 .size = 3, 76 .xstride = { 4 }, 77 .pstride = { 5 }, 78 .default_color = 6, 79 .chroma_key = 7, 80 .chroma_key_mask = 8, 81 .general_config = 9, 82 }, 83 }, 84 { 85 .name = "high-end-overlay", 86 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, 87 .regs_offset = 0x340, 88 .id = 3, 89 .type = ATMEL_HLCDC_OVERLAY_LAYER, 90 .nconfigs = 42, 91 .layout = { 92 .pos = 2, 93 .size = 3, 94 .memsize = 4, 95 .xstride = { 5, 7 }, 96 .pstride = { 6, 8 }, 97 .default_color = 9, 98 .chroma_key = 10, 99 .chroma_key_mask = 11, 100 .general_config = 12, 101 .csc = 14, 102 }, 103 }, 104 { 105 .name = "cursor", 106 .formats = &atmel_hlcdc_plane_rgb_formats, 107 .regs_offset = 0x440, 108 .id = 4, 109 .type = ATMEL_HLCDC_CURSOR_LAYER, 110 .nconfigs = 10, 111 .max_width = 128, 112 .max_height = 128, 113 .layout = { 114 .pos = 2, 115 .size = 3, 116 .xstride = { 4 }, 117 .pstride = { 5 }, 118 .default_color = 6, 119 .chroma_key = 7, 120 .chroma_key_mask = 8, 121 .general_config = 9, 122 }, 123 }, 124 }; 125 126 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { 127 .min_width = 0, 128 .min_height = 0, 129 .max_width = 2048, 130 .max_height = 2048, 131 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), 132 .layers = atmel_hlcdc_sama5d3_layers, 133 }; 134 135 static const struct of_device_id atmel_hlcdc_of_match[] = { 136 { 137 .compatible = "atmel,sama5d3-hlcdc", 138 .data = &atmel_hlcdc_dc_sama5d3, 139 }, 140 { /* sentinel */ }, 141 }; 142 143 int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 144 struct drm_display_mode *mode) 145 { 146 int vfront_porch = mode->vsync_start - mode->vdisplay; 147 int vback_porch = mode->vtotal - mode->vsync_end; 148 int vsync_len = mode->vsync_end - mode->vsync_start; 149 int hfront_porch = mode->hsync_start - mode->hdisplay; 150 int hback_porch = mode->htotal - mode->hsync_end; 151 int hsync_len = mode->hsync_end - mode->hsync_start; 152 153 if (hsync_len > 0x40 || hsync_len < 1) 154 return MODE_HSYNC; 155 156 if (vsync_len > 0x40 || vsync_len < 1) 157 return MODE_VSYNC; 158 159 if (hfront_porch > 0x200 || hfront_porch < 1 || 160 hback_porch > 0x200 || hback_porch < 1 || 161 mode->hdisplay < 1) 162 return MODE_H_ILLEGAL; 163 164 if (vfront_porch > 0x40 || vfront_porch < 1 || 165 vback_porch > 0x40 || vback_porch < 0 || 166 mode->vdisplay < 1) 167 return MODE_V_ILLEGAL; 168 169 return MODE_OK; 170 } 171 172 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 173 { 174 struct drm_device *dev = data; 175 struct atmel_hlcdc_dc *dc = dev->dev_private; 176 unsigned long status; 177 unsigned int imr, isr; 178 int i; 179 180 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 181 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 182 status = imr & isr; 183 if (!status) 184 return IRQ_NONE; 185 186 if (status & ATMEL_HLCDC_SOF) 187 atmel_hlcdc_crtc_irq(dc->crtc); 188 189 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 190 struct atmel_hlcdc_layer *layer = dc->layers[i]; 191 192 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer) 193 continue; 194 195 atmel_hlcdc_layer_irq(layer); 196 } 197 198 return IRQ_HANDLED; 199 } 200 201 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 202 struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) 203 { 204 return drm_fb_cma_create(dev, file_priv, mode_cmd); 205 } 206 207 static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 208 { 209 struct atmel_hlcdc_dc *dc = dev->dev_private; 210 211 if (dc->fbdev) { 212 drm_fbdev_cma_hotplug_event(dc->fbdev); 213 } else { 214 dc->fbdev = drm_fbdev_cma_init(dev, 24, 215 dev->mode_config.num_crtc, 216 dev->mode_config.num_connector); 217 if (IS_ERR(dc->fbdev)) 218 dc->fbdev = NULL; 219 } 220 } 221 222 static const struct drm_mode_config_funcs mode_config_funcs = { 223 .fb_create = atmel_hlcdc_fb_create, 224 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 225 .atomic_check = drm_atomic_helper_check, 226 .atomic_commit = drm_atomic_helper_commit, 227 }; 228 229 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 230 { 231 struct atmel_hlcdc_dc *dc = dev->dev_private; 232 struct atmel_hlcdc_planes *planes; 233 int ret; 234 int i; 235 236 drm_mode_config_init(dev); 237 238 ret = atmel_hlcdc_create_outputs(dev); 239 if (ret) { 240 dev_err(dev->dev, "failed to create panel: %d\n", ret); 241 return ret; 242 } 243 244 planes = atmel_hlcdc_create_planes(dev); 245 if (IS_ERR(planes)) { 246 dev_err(dev->dev, "failed to create planes\n"); 247 return PTR_ERR(planes); 248 } 249 250 dc->planes = planes; 251 252 dc->layers[planes->primary->layer.desc->id] = 253 &planes->primary->layer; 254 255 if (planes->cursor) 256 dc->layers[planes->cursor->layer.desc->id] = 257 &planes->cursor->layer; 258 259 for (i = 0; i < planes->noverlays; i++) 260 dc->layers[planes->overlays[i]->layer.desc->id] = 261 &planes->overlays[i]->layer; 262 263 ret = atmel_hlcdc_crtc_create(dev); 264 if (ret) { 265 dev_err(dev->dev, "failed to create crtc\n"); 266 return ret; 267 } 268 269 dev->mode_config.min_width = dc->desc->min_width; 270 dev->mode_config.min_height = dc->desc->min_height; 271 dev->mode_config.max_width = dc->desc->max_width; 272 dev->mode_config.max_height = dc->desc->max_height; 273 dev->mode_config.funcs = &mode_config_funcs; 274 275 return 0; 276 } 277 278 static int atmel_hlcdc_dc_load(struct drm_device *dev) 279 { 280 struct platform_device *pdev = to_platform_device(dev->dev); 281 const struct of_device_id *match; 282 struct atmel_hlcdc_dc *dc; 283 int ret; 284 285 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 286 if (!match) { 287 dev_err(&pdev->dev, "invalid compatible string\n"); 288 return -ENODEV; 289 } 290 291 if (!match->data) { 292 dev_err(&pdev->dev, "invalid hlcdc description\n"); 293 return -EINVAL; 294 } 295 296 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 297 if (!dc) 298 return -ENOMEM; 299 300 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 301 if (!dc->wq) 302 return -ENOMEM; 303 304 dc->desc = match->data; 305 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 306 dev->dev_private = dc; 307 308 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 309 if (ret) { 310 dev_err(dev->dev, "failed to enable periph_clk\n"); 311 goto err_destroy_wq; 312 } 313 314 pm_runtime_enable(dev->dev); 315 316 ret = atmel_hlcdc_dc_modeset_init(dev); 317 if (ret < 0) { 318 dev_err(dev->dev, "failed to initialize mode setting\n"); 319 goto err_periph_clk_disable; 320 } 321 322 drm_mode_config_reset(dev); 323 324 ret = drm_vblank_init(dev, 1); 325 if (ret < 0) { 326 dev_err(dev->dev, "failed to initialize vblank\n"); 327 goto err_periph_clk_disable; 328 } 329 330 pm_runtime_get_sync(dev->dev); 331 ret = drm_irq_install(dev, dc->hlcdc->irq); 332 pm_runtime_put_sync(dev->dev); 333 if (ret < 0) { 334 dev_err(dev->dev, "failed to install IRQ handler\n"); 335 goto err_periph_clk_disable; 336 } 337 338 platform_set_drvdata(pdev, dev); 339 340 drm_kms_helper_poll_init(dev); 341 342 /* force connectors detection */ 343 drm_helper_hpd_irq_event(dev); 344 345 return 0; 346 347 err_periph_clk_disable: 348 pm_runtime_disable(dev->dev); 349 clk_disable_unprepare(dc->hlcdc->periph_clk); 350 351 err_destroy_wq: 352 destroy_workqueue(dc->wq); 353 354 return ret; 355 } 356 357 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 358 { 359 struct atmel_hlcdc_dc *dc = dev->dev_private; 360 361 if (dc->fbdev) 362 drm_fbdev_cma_fini(dc->fbdev); 363 flush_workqueue(dc->wq); 364 drm_kms_helper_poll_fini(dev); 365 drm_mode_config_cleanup(dev); 366 drm_vblank_cleanup(dev); 367 368 pm_runtime_get_sync(dev->dev); 369 drm_irq_uninstall(dev); 370 pm_runtime_put_sync(dev->dev); 371 372 dev->dev_private = NULL; 373 374 pm_runtime_disable(dev->dev); 375 clk_disable_unprepare(dc->hlcdc->periph_clk); 376 destroy_workqueue(dc->wq); 377 } 378 379 static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 380 { 381 struct drm_connector *connector, *failed; 382 int ret; 383 384 mutex_lock(&dev->mode_config.mutex); 385 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 386 ret = drm_connector_register(connector); 387 if (ret) { 388 failed = connector; 389 goto err; 390 } 391 } 392 mutex_unlock(&dev->mode_config.mutex); 393 return 0; 394 395 err: 396 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 397 if (failed == connector) 398 break; 399 400 drm_connector_unregister(connector); 401 } 402 mutex_unlock(&dev->mode_config.mutex); 403 404 return ret; 405 } 406 407 static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 408 { 409 mutex_lock(&dev->mode_config.mutex); 410 drm_connector_unplug_all(dev); 411 mutex_unlock(&dev->mode_config.mutex); 412 } 413 414 static void atmel_hlcdc_dc_preclose(struct drm_device *dev, 415 struct drm_file *file) 416 { 417 struct drm_crtc *crtc; 418 419 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 420 atmel_hlcdc_crtc_cancel_page_flip(crtc, file); 421 } 422 423 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 424 { 425 struct atmel_hlcdc_dc *dc = dev->dev_private; 426 427 drm_fbdev_cma_restore_mode(dc->fbdev); 428 } 429 430 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 431 { 432 struct atmel_hlcdc_dc *dc = dev->dev_private; 433 unsigned int cfg = 0; 434 int i; 435 436 /* Enable interrupts on activated layers */ 437 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 438 if (dc->layers[i]) 439 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 440 } 441 442 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 443 444 return 0; 445 } 446 447 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 448 { 449 struct atmel_hlcdc_dc *dc = dev->dev_private; 450 unsigned int isr; 451 452 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 453 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 454 } 455 456 static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc) 457 { 458 struct atmel_hlcdc_dc *dc = dev->dev_private; 459 460 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 461 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 462 463 return 0; 464 } 465 466 static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc) 467 { 468 struct atmel_hlcdc_dc *dc = dev->dev_private; 469 470 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 471 } 472 473 static const struct file_operations fops = { 474 .owner = THIS_MODULE, 475 .open = drm_open, 476 .release = drm_release, 477 .unlocked_ioctl = drm_ioctl, 478 #ifdef CONFIG_COMPAT 479 .compat_ioctl = drm_compat_ioctl, 480 #endif 481 .poll = drm_poll, 482 .read = drm_read, 483 .llseek = no_llseek, 484 .mmap = drm_gem_cma_mmap, 485 }; 486 487 static struct drm_driver atmel_hlcdc_dc_driver = { 488 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, 489 .preclose = atmel_hlcdc_dc_preclose, 490 .lastclose = atmel_hlcdc_dc_lastclose, 491 .irq_handler = atmel_hlcdc_dc_irq_handler, 492 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 493 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 494 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 495 .get_vblank_counter = drm_vblank_count, 496 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 497 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 498 .gem_free_object = drm_gem_cma_free_object, 499 .gem_vm_ops = &drm_gem_cma_vm_ops, 500 .dumb_create = drm_gem_cma_dumb_create, 501 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 502 .dumb_destroy = drm_gem_dumb_destroy, 503 .fops = &fops, 504 .name = "atmel-hlcdc", 505 .desc = "Atmel HLCD Controller DRM", 506 .date = "20141504", 507 .major = 1, 508 .minor = 0, 509 }; 510 511 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 512 { 513 struct drm_device *ddev; 514 int ret; 515 516 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 517 if (!ddev) 518 return -ENOMEM; 519 520 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev)); 521 if (ret) 522 goto err_unref; 523 524 ret = atmel_hlcdc_dc_load(ddev); 525 if (ret) 526 goto err_unref; 527 528 ret = drm_dev_register(ddev, 0); 529 if (ret) 530 goto err_unload; 531 532 ret = atmel_hlcdc_dc_connector_plug_all(ddev); 533 if (ret) 534 goto err_unregister; 535 536 return 0; 537 538 err_unregister: 539 drm_dev_unregister(ddev); 540 541 err_unload: 542 atmel_hlcdc_dc_unload(ddev); 543 544 err_unref: 545 drm_dev_unref(ddev); 546 547 return ret; 548 } 549 550 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 551 { 552 struct drm_device *ddev = platform_get_drvdata(pdev); 553 554 atmel_hlcdc_dc_connector_unplug_all(ddev); 555 drm_dev_unregister(ddev); 556 atmel_hlcdc_dc_unload(ddev); 557 drm_dev_unref(ddev); 558 559 return 0; 560 } 561 562 #ifdef CONFIG_PM 563 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 564 { 565 struct drm_device *drm_dev = dev_get_drvdata(dev); 566 struct drm_crtc *crtc; 567 568 if (pm_runtime_suspended(dev)) 569 return 0; 570 571 drm_modeset_lock_all(drm_dev); 572 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 573 atmel_hlcdc_crtc_suspend(crtc); 574 drm_modeset_unlock_all(drm_dev); 575 return 0; 576 } 577 578 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 579 { 580 struct drm_device *drm_dev = dev_get_drvdata(dev); 581 struct drm_crtc *crtc; 582 583 if (pm_runtime_suspended(dev)) 584 return 0; 585 586 drm_modeset_lock_all(drm_dev); 587 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 588 atmel_hlcdc_crtc_resume(crtc); 589 drm_modeset_unlock_all(drm_dev); 590 return 0; 591 } 592 #endif 593 594 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 595 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 596 597 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 598 { .compatible = "atmel,hlcdc-display-controller" }, 599 { }, 600 }; 601 602 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 603 .probe = atmel_hlcdc_dc_drm_probe, 604 .remove = atmel_hlcdc_dc_drm_remove, 605 .driver = { 606 .name = "atmel-hlcdc-display-controller", 607 .pm = &atmel_hlcdc_dc_drm_pm_ops, 608 .of_match_table = atmel_hlcdc_dc_of_match, 609 }, 610 }; 611 module_platform_driver(atmel_hlcdc_dc_platform_driver); 612 613 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 614 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 615 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 616 MODULE_LICENSE("GPL"); 617 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 618