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 pm_runtime_put_sync(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 ret = drm_vblank_init(dev, 1); 323 if (ret < 0) { 324 dev_err(dev->dev, "failed to initialize vblank\n"); 325 goto err_periph_clk_disable; 326 } 327 328 pm_runtime_get_sync(dev->dev); 329 ret = drm_irq_install(dev, dc->hlcdc->irq); 330 pm_runtime_put_sync(dev->dev); 331 if (ret < 0) { 332 dev_err(dev->dev, "failed to install IRQ handler\n"); 333 goto err_periph_clk_disable; 334 } 335 336 platform_set_drvdata(pdev, dev); 337 338 drm_kms_helper_poll_init(dev); 339 340 /* force connectors detection */ 341 drm_helper_hpd_irq_event(dev); 342 343 return 0; 344 345 err_periph_clk_disable: 346 pm_runtime_disable(dev->dev); 347 clk_disable_unprepare(dc->hlcdc->periph_clk); 348 349 err_destroy_wq: 350 destroy_workqueue(dc->wq); 351 352 return ret; 353 } 354 355 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 356 { 357 struct atmel_hlcdc_dc *dc = dev->dev_private; 358 359 if (dc->fbdev) 360 drm_fbdev_cma_fini(dc->fbdev); 361 flush_workqueue(dc->wq); 362 drm_kms_helper_poll_fini(dev); 363 drm_mode_config_cleanup(dev); 364 drm_vblank_cleanup(dev); 365 366 pm_runtime_get_sync(dev->dev); 367 drm_irq_uninstall(dev); 368 pm_runtime_put_sync(dev->dev); 369 370 dev->dev_private = NULL; 371 372 pm_runtime_disable(dev->dev); 373 clk_disable_unprepare(dc->hlcdc->periph_clk); 374 destroy_workqueue(dc->wq); 375 } 376 377 static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 378 { 379 struct drm_connector *connector, *failed; 380 int ret; 381 382 mutex_lock(&dev->mode_config.mutex); 383 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 384 ret = drm_connector_register(connector); 385 if (ret) { 386 failed = connector; 387 goto err; 388 } 389 } 390 mutex_unlock(&dev->mode_config.mutex); 391 return 0; 392 393 err: 394 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 395 if (failed == connector) 396 break; 397 398 drm_connector_unregister(connector); 399 } 400 mutex_unlock(&dev->mode_config.mutex); 401 402 return ret; 403 } 404 405 static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 406 { 407 mutex_lock(&dev->mode_config.mutex); 408 drm_connector_unplug_all(dev); 409 mutex_unlock(&dev->mode_config.mutex); 410 } 411 412 static void atmel_hlcdc_dc_preclose(struct drm_device *dev, 413 struct drm_file *file) 414 { 415 struct drm_crtc *crtc; 416 417 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 418 atmel_hlcdc_crtc_cancel_page_flip(crtc, file); 419 } 420 421 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 422 { 423 struct atmel_hlcdc_dc *dc = dev->dev_private; 424 425 drm_fbdev_cma_restore_mode(dc->fbdev); 426 } 427 428 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 429 { 430 struct atmel_hlcdc_dc *dc = dev->dev_private; 431 unsigned int cfg = 0; 432 int i; 433 434 /* Enable interrupts on activated layers */ 435 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 436 if (dc->layers[i]) 437 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 438 } 439 440 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 441 442 return 0; 443 } 444 445 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 446 { 447 struct atmel_hlcdc_dc *dc = dev->dev_private; 448 unsigned int isr; 449 450 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 451 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 452 } 453 454 static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc) 455 { 456 struct atmel_hlcdc_dc *dc = dev->dev_private; 457 458 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 459 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 460 461 return 0; 462 } 463 464 static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc) 465 { 466 struct atmel_hlcdc_dc *dc = dev->dev_private; 467 468 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 469 } 470 471 static const struct file_operations fops = { 472 .owner = THIS_MODULE, 473 .open = drm_open, 474 .release = drm_release, 475 .unlocked_ioctl = drm_ioctl, 476 #ifdef CONFIG_COMPAT 477 .compat_ioctl = drm_compat_ioctl, 478 #endif 479 .poll = drm_poll, 480 .read = drm_read, 481 .llseek = no_llseek, 482 .mmap = drm_gem_cma_mmap, 483 }; 484 485 static struct drm_driver atmel_hlcdc_dc_driver = { 486 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, 487 .preclose = atmel_hlcdc_dc_preclose, 488 .lastclose = atmel_hlcdc_dc_lastclose, 489 .irq_handler = atmel_hlcdc_dc_irq_handler, 490 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 491 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 492 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 493 .get_vblank_counter = drm_vblank_count, 494 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 495 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 496 .gem_free_object = drm_gem_cma_free_object, 497 .gem_vm_ops = &drm_gem_cma_vm_ops, 498 .dumb_create = drm_gem_cma_dumb_create, 499 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 500 .dumb_destroy = drm_gem_dumb_destroy, 501 .fops = &fops, 502 .name = "atmel-hlcdc", 503 .desc = "Atmel HLCD Controller DRM", 504 .date = "20141504", 505 .major = 1, 506 .minor = 0, 507 }; 508 509 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 510 { 511 struct drm_device *ddev; 512 int ret; 513 514 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 515 if (!ddev) 516 return -ENOMEM; 517 518 ret = drm_dev_set_unique(ddev, dev_name(ddev->dev)); 519 if (ret) 520 goto err_unref; 521 522 ret = atmel_hlcdc_dc_load(ddev); 523 if (ret) 524 goto err_unref; 525 526 ret = drm_dev_register(ddev, 0); 527 if (ret) 528 goto err_unload; 529 530 ret = atmel_hlcdc_dc_connector_plug_all(ddev); 531 if (ret) 532 goto err_unregister; 533 534 return 0; 535 536 err_unregister: 537 drm_dev_unregister(ddev); 538 539 err_unload: 540 atmel_hlcdc_dc_unload(ddev); 541 542 err_unref: 543 drm_dev_unref(ddev); 544 545 return ret; 546 } 547 548 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 549 { 550 struct drm_device *ddev = platform_get_drvdata(pdev); 551 552 atmel_hlcdc_dc_connector_unplug_all(ddev); 553 drm_dev_unregister(ddev); 554 atmel_hlcdc_dc_unload(ddev); 555 drm_dev_unref(ddev); 556 557 return 0; 558 } 559 560 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 561 { .compatible = "atmel,hlcdc-display-controller" }, 562 { }, 563 }; 564 565 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 566 .probe = atmel_hlcdc_dc_drm_probe, 567 .remove = atmel_hlcdc_dc_drm_remove, 568 .driver = { 569 .name = "atmel-hlcdc-display-controller", 570 .of_match_table = atmel_hlcdc_dc_of_match, 571 }, 572 }; 573 module_platform_driver(atmel_hlcdc_dc_platform_driver); 574 575 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 576 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 577 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 578 MODULE_LICENSE("GPL"); 579 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 580