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 }; 226 227 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 228 { 229 struct atmel_hlcdc_dc *dc = dev->dev_private; 230 struct atmel_hlcdc_planes *planes; 231 int ret; 232 int i; 233 234 drm_mode_config_init(dev); 235 236 ret = atmel_hlcdc_create_outputs(dev); 237 if (ret) { 238 dev_err(dev->dev, "failed to create panel: %d\n", ret); 239 return ret; 240 } 241 242 planes = atmel_hlcdc_create_planes(dev); 243 if (IS_ERR(planes)) { 244 dev_err(dev->dev, "failed to create planes\n"); 245 return PTR_ERR(planes); 246 } 247 248 dc->planes = planes; 249 250 dc->layers[planes->primary->layer.desc->id] = 251 &planes->primary->layer; 252 253 if (planes->cursor) 254 dc->layers[planes->cursor->layer.desc->id] = 255 &planes->cursor->layer; 256 257 for (i = 0; i < planes->noverlays; i++) 258 dc->layers[planes->overlays[i]->layer.desc->id] = 259 &planes->overlays[i]->layer; 260 261 ret = atmel_hlcdc_crtc_create(dev); 262 if (ret) { 263 dev_err(dev->dev, "failed to create crtc\n"); 264 return ret; 265 } 266 267 dev->mode_config.min_width = dc->desc->min_width; 268 dev->mode_config.min_height = dc->desc->min_height; 269 dev->mode_config.max_width = dc->desc->max_width; 270 dev->mode_config.max_height = dc->desc->max_height; 271 dev->mode_config.funcs = &mode_config_funcs; 272 273 return 0; 274 } 275 276 static int atmel_hlcdc_dc_load(struct drm_device *dev) 277 { 278 struct platform_device *pdev = to_platform_device(dev->dev); 279 const struct of_device_id *match; 280 struct atmel_hlcdc_dc *dc; 281 int ret; 282 283 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 284 if (!match) { 285 dev_err(&pdev->dev, "invalid compatible string\n"); 286 return -ENODEV; 287 } 288 289 if (!match->data) { 290 dev_err(&pdev->dev, "invalid hlcdc description\n"); 291 return -EINVAL; 292 } 293 294 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 295 if (!dc) 296 return -ENOMEM; 297 298 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 299 if (!dc->wq) 300 return -ENOMEM; 301 302 dc->desc = match->data; 303 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 304 dev->dev_private = dc; 305 306 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 307 if (ret) { 308 dev_err(dev->dev, "failed to enable periph_clk\n"); 309 goto err_destroy_wq; 310 } 311 312 pm_runtime_enable(dev->dev); 313 314 ret = atmel_hlcdc_dc_modeset_init(dev); 315 if (ret < 0) { 316 dev_err(dev->dev, "failed to initialize mode setting\n"); 317 goto err_periph_clk_disable; 318 } 319 320 ret = drm_vblank_init(dev, 1); 321 if (ret < 0) { 322 dev_err(dev->dev, "failed to initialize vblank\n"); 323 goto err_periph_clk_disable; 324 } 325 326 pm_runtime_get_sync(dev->dev); 327 ret = drm_irq_install(dev, dc->hlcdc->irq); 328 pm_runtime_put_sync(dev->dev); 329 if (ret < 0) { 330 dev_err(dev->dev, "failed to install IRQ handler\n"); 331 goto err_periph_clk_disable; 332 } 333 334 platform_set_drvdata(pdev, dev); 335 336 drm_kms_helper_poll_init(dev); 337 338 /* force connectors detection */ 339 drm_helper_hpd_irq_event(dev); 340 341 return 0; 342 343 err_periph_clk_disable: 344 pm_runtime_disable(dev->dev); 345 clk_disable_unprepare(dc->hlcdc->periph_clk); 346 347 err_destroy_wq: 348 destroy_workqueue(dc->wq); 349 350 return ret; 351 } 352 353 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 354 { 355 struct atmel_hlcdc_dc *dc = dev->dev_private; 356 357 if (dc->fbdev) 358 drm_fbdev_cma_fini(dc->fbdev); 359 flush_workqueue(dc->wq); 360 drm_kms_helper_poll_fini(dev); 361 drm_mode_config_cleanup(dev); 362 drm_vblank_cleanup(dev); 363 364 pm_runtime_get_sync(dev->dev); 365 drm_irq_uninstall(dev); 366 pm_runtime_put_sync(dev->dev); 367 368 dev->dev_private = NULL; 369 370 pm_runtime_disable(dev->dev); 371 clk_disable_unprepare(dc->hlcdc->periph_clk); 372 destroy_workqueue(dc->wq); 373 } 374 375 static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 376 { 377 struct drm_connector *connector, *failed; 378 int ret; 379 380 mutex_lock(&dev->mode_config.mutex); 381 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 382 ret = drm_connector_register(connector); 383 if (ret) { 384 failed = connector; 385 goto err; 386 } 387 } 388 mutex_unlock(&dev->mode_config.mutex); 389 return 0; 390 391 err: 392 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 393 if (failed == connector) 394 break; 395 396 drm_connector_unregister(connector); 397 } 398 mutex_unlock(&dev->mode_config.mutex); 399 400 return ret; 401 } 402 403 static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 404 { 405 mutex_lock(&dev->mode_config.mutex); 406 drm_connector_unplug_all(dev); 407 mutex_unlock(&dev->mode_config.mutex); 408 } 409 410 static void atmel_hlcdc_dc_preclose(struct drm_device *dev, 411 struct drm_file *file) 412 { 413 struct drm_crtc *crtc; 414 415 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 416 atmel_hlcdc_crtc_cancel_page_flip(crtc, file); 417 } 418 419 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 420 { 421 struct atmel_hlcdc_dc *dc = dev->dev_private; 422 423 drm_fbdev_cma_restore_mode(dc->fbdev); 424 } 425 426 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 427 { 428 struct atmel_hlcdc_dc *dc = dev->dev_private; 429 unsigned int cfg = 0; 430 int i; 431 432 /* Enable interrupts on activated layers */ 433 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 434 if (dc->layers[i]) 435 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 436 } 437 438 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 439 440 return 0; 441 } 442 443 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 444 { 445 struct atmel_hlcdc_dc *dc = dev->dev_private; 446 unsigned int isr; 447 448 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 449 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 450 } 451 452 static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc) 453 { 454 struct atmel_hlcdc_dc *dc = dev->dev_private; 455 456 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 457 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 458 459 return 0; 460 } 461 462 static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc) 463 { 464 struct atmel_hlcdc_dc *dc = dev->dev_private; 465 466 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 467 } 468 469 static const struct file_operations fops = { 470 .owner = THIS_MODULE, 471 .open = drm_open, 472 .release = drm_release, 473 .unlocked_ioctl = drm_ioctl, 474 #ifdef CONFIG_COMPAT 475 .compat_ioctl = drm_compat_ioctl, 476 #endif 477 .poll = drm_poll, 478 .read = drm_read, 479 .llseek = no_llseek, 480 .mmap = drm_gem_cma_mmap, 481 }; 482 483 static struct drm_driver atmel_hlcdc_dc_driver = { 484 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, 485 .preclose = atmel_hlcdc_dc_preclose, 486 .lastclose = atmel_hlcdc_dc_lastclose, 487 .irq_handler = atmel_hlcdc_dc_irq_handler, 488 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 489 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 490 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 491 .get_vblank_counter = drm_vblank_count, 492 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 493 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 494 .gem_free_object = drm_gem_cma_free_object, 495 .gem_vm_ops = &drm_gem_cma_vm_ops, 496 .dumb_create = drm_gem_cma_dumb_create, 497 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 498 .dumb_destroy = drm_gem_dumb_destroy, 499 .fops = &fops, 500 .name = "atmel-hlcdc", 501 .desc = "Atmel HLCD Controller DRM", 502 .date = "20141504", 503 .major = 1, 504 .minor = 0, 505 }; 506 507 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 508 { 509 struct drm_device *ddev; 510 int ret; 511 512 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 513 if (!ddev) 514 return -ENOMEM; 515 516 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev)); 517 if (ret) 518 goto err_unref; 519 520 ret = atmel_hlcdc_dc_load(ddev); 521 if (ret) 522 goto err_unref; 523 524 ret = drm_dev_register(ddev, 0); 525 if (ret) 526 goto err_unload; 527 528 ret = atmel_hlcdc_dc_connector_plug_all(ddev); 529 if (ret) 530 goto err_unregister; 531 532 return 0; 533 534 err_unregister: 535 drm_dev_unregister(ddev); 536 537 err_unload: 538 atmel_hlcdc_dc_unload(ddev); 539 540 err_unref: 541 drm_dev_unref(ddev); 542 543 return ret; 544 } 545 546 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 547 { 548 struct drm_device *ddev = platform_get_drvdata(pdev); 549 550 atmel_hlcdc_dc_connector_unplug_all(ddev); 551 drm_dev_unregister(ddev); 552 atmel_hlcdc_dc_unload(ddev); 553 drm_dev_unref(ddev); 554 555 return 0; 556 } 557 558 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 559 { .compatible = "atmel,hlcdc-display-controller" }, 560 { }, 561 }; 562 563 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 564 .probe = atmel_hlcdc_dc_drm_probe, 565 .remove = atmel_hlcdc_dc_drm_remove, 566 .driver = { 567 .name = "atmel-hlcdc-display-controller", 568 .of_match_table = atmel_hlcdc_dc_of_match, 569 }, 570 }; 571 module_platform_driver(atmel_hlcdc_dc_platform_driver); 572 573 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 574 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 575 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 576 MODULE_LICENSE("GPL"); 577 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 578