1/* 2 * Copyright (c) 2018 Loongson Technology Co., Ltd. 3 * Authors: 4 * Chen Zhu <zhuchen@loongson.cn> 5 * Yaling Fang <fangyaling@loongson.cn> 6 * Dandan Zhang <zhangdandan@loongson.cn> 7 * Huacai Chen <chenhc@lemote.com> 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14#include <asm/addrspace.h> 15#include <linux/dma-mapping.h> 16#include <linux/vmalloc.h> 17#include <linux/console.h> 18#include <linux/device.h> 19#include <linux/slab.h> 20#include <linux/fs.h> 21#include <linux/pci.h> 22#include <linux/pci_ids.h> 23#include <linux/platform_device.h> 24#include <linux/module.h> 25#include <linux/kernel.h> 26#include <linux/errno.h> 27#include <linux/init.h> 28#include <linux/string.h> 29#include <linux/vga_switcheroo.h> 30 31#include <drm/drm_atomic_helper.h> 32#include <drm/drm_crtc_helper.h> 33#include <drm/drm_probe_helper.h> 34#include <drm/drm_gem_cma_helper.h> 35#include <drm/drm_gem_framebuffer_helper.h> 36#include <loongson.h> 37#include "loongson_drv.h" 38 39#define DEVICE_NAME "loongson-drm" 40#define DRIVER_NAME "loongson-drm" 41#define DRIVER_DESC "Loongson DRM Driver" 42#define DRIVER_DATE "20201201" 43#define DRIVER_MAJOR 1 44#define DRIVER_MINOR 0 45#define DRIVER_PATCHLEVEL 1 46 47bool hw_cursor = false; 48module_param_named(cursor, hw_cursor, bool, 0600); 49 50bool poll_connector = false; 51module_param_named(poll, poll_connector, bool, 0600); 52 53DEFINE_SPINLOCK(loongson_reglock); 54 55/** 56 * loongson_mode_funcs---basic driver provided mode setting functions 57 * 58 * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that 59 * involve drivers. 60 */ 61static const struct drm_mode_config_funcs loongson_mode_funcs = { 62 .fb_create = drm_gem_fb_create, 63 .atomic_check = drm_atomic_helper_check, 64 .atomic_commit = drm_atomic_helper_commit, 65 .output_poll_changed = drm_fb_helper_output_poll_changed 66}; 67 68/** 69 * loongson_drm_device_init ----init drm device 70 * 71 * @dev pointer to drm_device structure 72 * @flags start up flag 73 * 74 * RETURN 75 * drm device init result 76 */ 77static int loongson_drm_device_init(struct drm_device *dev, uint32_t flags) 78{ 79 int ret = 0; 80 struct loongson_drm_device *ldev = dev->dev_private; 81 82 ldev->num_crtc = 2; 83 loongson_vbios_init(ldev); 84 85 /*BAR 0 contains registers */ 86 ldev->mmio_base = pci_resource_start(ldev->dev->pdev, 0); 87 ldev->mmio_size = pci_resource_len(ldev->dev->pdev, 0); 88 89 ldev->mmio = pcim_iomap(dev->pdev, 0, 0); 90 if (ldev->mmio == NULL) 91 return -ENOMEM; 92 93 DRM_INFO("ldev->mmio_base = 0x%llx, ldev->mmio_size = 0x%llx\n", 94 ldev->mmio_base, ldev->mmio_size); 95 96 if (!devm_request_mem_region(ldev->dev->dev, ldev->mmio_base, ldev->mmio_size, 97 "loongson_drmfb_mmio")) { 98 DRM_ERROR("Can't reserve mmio registers\n"); 99 return -ENOMEM; 100 } 101 102 ret = loongson_gpio_init(ldev); 103 if (ret < 0) 104 DRM_ERROR("Failed to initialize dc gpios\n"); 105 106 return ret; 107} 108 109/** 110 * loongson_modeset_init --- init kernel mode setting 111 * 112 * @ldev: pointer to loongson_drm_device structure 113 * 114 * RETURN 115 * return init result 116 */ 117int loongson_modeset_init(struct loongson_drm_device *ldev) 118{ 119 int i, ret; 120 struct drm_encoder *encoder; 121 struct drm_connector *connector; 122 123 ldev->mode_info[0].mode_config_initialized = true; 124 ldev->mode_info[1].mode_config_initialized = true; 125 126 ldev->dev->mode_config.max_width = LOONGSON_MAX_FB_WIDTH; 127 ldev->dev->mode_config.max_height = LOONGSON_MAX_FB_HEIGHT; 128 129 ldev->dev->mode_config.cursor_width = 32; 130 ldev->dev->mode_config.cursor_height = 32; 131 132 ldev->dev->mode_config.allow_fb_modifiers = true; 133 134 ret = loongson_i2c_init(ldev); 135 if (ret < 0) { 136 DRM_ERROR("Failed to initialize i2c\n"); 137 return ret; 138 } 139 140 loongson_crtc_init(ldev); 141 142 for (i=0; i<ldev->num_crtc; i++) { 143 DRM_DEBUG("loongson drm encoder init\n"); 144 ldev->mode_info[i].crtc = &ldev->lcrtc[i]; 145 encoder = loongson_encoder_init(ldev->dev, i); 146 if (!encoder) { 147 DRM_ERROR("loongson_encoder_init failed\n"); 148 return -1; 149 } 150 ldev->mode_info[i].encoder = to_loongson_encoder(encoder); 151 152 DRM_DEBUG("loongson drm connector init\n"); 153 connector = loongson_connector_init(ldev->dev, i); 154 if (!connector) { 155 DRM_ERROR("loongson_connector_init failed\n"); 156 return -1; 157 } 158 ldev->mode_info[i].connector = to_loongson_connector(connector); 159 160 drm_connector_attach_encoder(connector, encoder); 161 if (poll_connector) 162 connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 163 } 164 165 return 0; 166} 167 168/** 169 * loongson_modeset_fini --- deinit kernel mode setting 170 * 171 * @ldev: pointer to loongson_drm_device structure 172 * 173 * RETURN 174 */ 175void loongson_modeset_fini(struct loongson_drm_device *ldev) 176{ 177} 178 179static int loongson_detect_chip(struct loongson_drm_device *ldev) 180{ 181 struct pci_dev *pdev; 182 183 pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, NULL); 184 if (pdev) { 185 ldev->chip = dc_7a1000; 186 ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a15, NULL); 187 DRM_INFO("Set LS7A1000 DC device\n"); 188 return 0; 189 } 190 191 pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, NULL); 192 if (pdev) { 193 ldev->chip = dc_7a2000; 194 ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a25, NULL); 195 DRM_INFO("Set LS7A2000 DC device\n"); 196 return 0; 197 } 198 199 return -1; 200} 201 202/** 203 * loongson_vga_load - setup chip and create an initial config 204 * @dev: DRM device 205 * @flags: startup flags 206 * 207 * The driver load routine has to do several things: 208 * - initialize the memory manager 209 * - allocate initial config memory 210 * - setup the DRM framebuffer with the allocated memory 211 */ 212static int loongson_drm_load(struct drm_device *dev, unsigned long flags) 213{ 214 int r, ret, irq; 215 struct loongson_drm_device *ldev; 216 217 dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32)); 218 219 ldev = devm_kzalloc(dev->dev, sizeof(struct loongson_drm_device), GFP_KERNEL); 220 if (ldev == NULL) 221 return -ENOMEM; 222 dev->dev_private = (void *)ldev; 223 ldev->dev = dev; 224 225 ret = loongson_detect_chip(ldev); 226 if (ret) 227 dev_err(dev->dev, "Fatal error during detect chip: %d\n", ret); 228 229 ret = loongson_drm_device_init(dev, flags); 230 DRM_DEBUG("end loongson drm device init.\n"); 231 232 drm_mode_config_init(dev); 233 dev->mode_config.funcs = (void *)&loongson_mode_funcs; 234 dev->mode_config.preferred_depth = 24; 235 dev->mode_config.prefer_shadow = 1; 236 237 irq = dev->pdev->irq; 238 dev_set_drvdata(dev->dev, dev); 239 pci_set_drvdata(dev->pdev, dev); 240 241 r = loongson_modeset_init(ldev); 242 if (r) 243 dev_err(dev->dev, "Fatal error during modeset init: %d\n", r); 244 245 r = drm_irq_install(dev, irq); 246 if (r) 247 dev_err(dev->dev, "Fatal error during irq install: %d\n", r); 248 249 ldev->inited = true; 250 drm_mode_config_reset(dev); 251 252 r = drm_vblank_init(dev, ldev->num_crtc); 253 if (r) 254 dev_err(dev->dev, "Fatal error during vblank init: %d\n", r); 255 256 /* Make small buffers to store a hardware cursor (double buffered icon updates) */ 257 ldev->cursor = drm_gem_cma_create(dev, roundup(32*32*4, PAGE_SIZE)); 258 259 drm_kms_helper_poll_init(dev); 260 261 drm_fb_helper_remove_conflicting_framebuffers(NULL, "loongson-drmfb", false); 262 263 return 0; 264} 265 266/** 267 * loongson_drm_unload--release drm resource 268 * 269 * @dev: pointer to drm_device 270 * 271 */ 272static void loongson_drm_unload(struct drm_device *dev) 273{ 274 struct loongson_drm_device *ldev = dev->dev_private; 275 276 if (ldev == NULL) 277 return; 278 279 loongson_modeset_fini(ldev); 280 drm_mode_config_cleanup(dev); 281 dev->dev_private = NULL; 282 dev_set_drvdata(dev->dev, NULL); 283 ldev->inited = false; 284 285 return; 286} 287 288/** 289 * loongson_drm_open -Driver callback when a new struct drm_file is opened. 290 * Useful for setting up driver-private data structures like buffer allocators, 291 * execution contexts or similar things. 292 * 293 * @dev DRM device 294 * @file DRM file private date 295 * 296 * RETURN 297 * 0 on success, a negative error code on failure, which will be promoted to 298 * userspace as the result of the open() system call. 299 */ 300static int loongson_drm_open(struct drm_device *dev, struct drm_file *file) 301{ 302 file->driver_priv = NULL; 303 304 DRM_DEBUG("open: dev=%p, file=%p", dev, file); 305 306 return 0; 307} 308 309DEFINE_DRM_GEM_CMA_FOPS(fops); 310 311/** 312 * loongson_drm_driver - DRM device structure 313 * 314 * .load: driver callback to complete initialization steps after the driver is registered 315 * .unload:Reverse the effects of the driver load callback 316 * .open:Driver callback when a new struct drm_file is opened 317 * .fops:File operations for the DRM device node. 318 * .gem_free_object:deconstructor for drm_gem_objects 319 * .dumb_create:This creates a new dumb buffer in the driver’s backing storage manager 320 * (GEM, TTM or something else entirely) and returns the resulting buffer handle. 321 * This handle can then be wrapped up into a framebuffer modeset object 322 * .dumb_map_offset:Allocate an offset in the drm device node’s address space 323 * to be able to memory map a dumb buffer 324 * .dump_destory:This destroys the userspace handle for the given dumb backing storage buffer 325 */ 326static struct drm_driver loongson_drm_driver = { 327 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_ATOMIC, 328 .open = loongson_drm_open, 329 .fops = &fops, 330 331 .dumb_create = drm_gem_cma_dumb_create, 332 .gem_free_object_unlocked = drm_gem_cma_free_object, 333 .gem_vm_ops = &drm_gem_cma_vm_ops, 334 335 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 336 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 337 338 .gem_prime_import = drm_gem_prime_import, 339 .gem_prime_export = drm_gem_prime_export, 340 341 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 342 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 343 .gem_prime_vmap = drm_gem_cma_prime_vmap, 344 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 345 .gem_prime_mmap = drm_gem_cma_prime_mmap, 346 347 .irq_handler = loongson_irq_handler, 348 .irq_preinstall = loongson_irq_preinstall, 349 .irq_postinstall = loongson_irq_postinstall, 350 .irq_uninstall = loongson_irq_uninstall, 351 .name = DRIVER_NAME, 352 .desc = DRIVER_DESC, 353 .date = DRIVER_DATE, 354 .major = DRIVER_MAJOR, 355 .minor = DRIVER_MINOR, 356 .patchlevel = DRIVER_PATCHLEVEL, 357}; 358 359/** 360 * loongson_drm_pci_devices -- pci device id info 361 * 362 * __u32 vendor, device Vendor and device ID or PCI_ANY_ID 363 * __u32 subvendor, subdevice Subsystem ID's or PCI_ANY_ID 364 * __u32 class, class_mask (class,subclass,prog-if) triplet 365 * kernel_ulong_t driver_data Data private to the driver 366 */ 367static struct pci_device_id loongson_drm_pci_devices[] = { 368 {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1)}, 369 {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2)}, 370 {0, 0, 0, 0, 0, 0, 0} 371}; 372 373MODULE_DEVICE_TABLE(pci, loongson_drm_pci_devices); 374 375/** 376 * loongson_drm_pci_register -- add pci device 377 * 378 * @pdev PCI device 379 * @ent pci device id 380 */ 381static int loongson_drm_pci_register(struct pci_dev *pdev, 382 const struct pci_device_id *ent) 383 384{ 385 int ret; 386 struct drm_device *dev; 387 388 dev = drm_dev_alloc(&loongson_drm_driver, &pdev->dev); 389 if (IS_ERR(dev)) 390 return PTR_ERR(dev); 391 392 ret = pci_enable_device(pdev); 393 if (ret) 394 goto err_free; 395 396 dev->pdev = pdev; 397 398 loongson_drm_load(dev, 0x0); 399 400 ret = drm_dev_register(dev, 0); 401 if (ret) 402 goto err_pdev; 403 404 drm_fbdev_generic_setup(dev, 32); 405 406 return 0; 407 408err_pdev: 409 pci_disable_device(pdev); 410err_free: 411 drm_dev_put(dev); 412 return ret; 413} 414 415/** 416 * loongson_drm_pci_unregister -- release drm device 417 * 418 * @pdev PCI device 419 */ 420static void loongson_drm_pci_unregister(struct pci_dev *pdev) 421{ 422 struct drm_device *dev = pci_get_drvdata(pdev); 423 loongson_drm_unload(dev); 424 drm_dev_put(dev); 425} 426 427/* 428 * Suspend & resume. 429 */ 430/* 431 * loongson_drm_suspend - initiate device suspend 432 * 433 * @pdev: drm dev pointer 434 * @state: suspend state 435 * 436 * Puts the hw in the suspend state (all asics). 437 * Returns 0 for success or an error on failure. 438 * Called at driver suspend. 439 */ 440int loongson_drm_suspend(struct drm_device *dev, bool suspend) 441{ 442 struct loongson_drm_device *ldev; 443 444 if (dev == NULL || dev->dev_private == NULL) 445 return -ENODEV; 446 447 ldev = dev->dev_private; 448 449 drm_kms_helper_poll_disable(dev); 450 ldev->state = drm_atomic_helper_suspend(dev); 451 452 pci_save_state(dev->pdev); 453 if (suspend) { 454 /* Shut down the device */ 455 pci_disable_device(dev->pdev); 456 pci_set_power_state(dev->pdev, PCI_D3hot); 457 } 458 459 console_lock(); 460 drm_fb_helper_set_suspend(ldev->dev->fb_helper, 1); 461 console_unlock(); 462 463 return 0; 464} 465 466/* 467 * * loongson_drm_resume - initiate device suspend 468 * 469 * @pdev: drm dev pointer 470 * @state: suspend state 471 * 472 * Puts the hw in the suspend state (all asics). 473 * Returns 0 for success or an error on failure. 474 * Called at driver suspend. 475 */ 476 477int loongson_drm_resume(struct drm_device *dev, bool resume) 478{ 479 struct loongson_drm_device *ldev = dev->dev_private; 480 481 console_lock(); 482 483 if (resume) { 484 pci_set_power_state(dev->pdev, PCI_D0); 485 pci_restore_state(dev->pdev); 486 if (pci_enable_device(dev->pdev)) { 487 console_unlock(); 488 return -1; 489 } 490 } 491 492 /* blat the mode back in */ 493 drm_atomic_helper_resume(dev, ldev->state); 494 495 drm_kms_helper_poll_enable(dev); 496 497 drm_fb_helper_set_suspend(ldev->dev->fb_helper, 0); 498 499 console_unlock(); 500 501 return 0; 502} 503 504/** 505 * loongson_drm_pm_suspend 506 * 507 * @dev pointer to the device 508 * 509 * Executed before putting the system into a sleep state in which the 510 * contents of main memory are preserved. 511 */ 512static int loongson_drm_pm_suspend(struct device *dev) 513{ 514 struct drm_device *drm_dev = dev_get_drvdata(dev); 515 516 return loongson_drm_suspend(drm_dev, true); 517} 518 519/** 520 * loongson_drm_pm_resume 521 * 522 * @dev pointer to the device 523 * 524 * Executed after waking the system up from a sleep state in which the 525 * contents of main memory were preserved. 526 */ 527static int loongson_drm_pm_resume(struct device *dev) 528{ 529 struct drm_device *drm_dev = dev_get_drvdata(dev); 530 531 return loongson_drm_resume(drm_dev, true); 532} 533 534/** 535 * loongson_drm_pm_freeze 536 * 537 * @dev pointer to device 538 * 539 * Hibernation-specific, executed before creating a hibernation image. 540 * Analogous to @suspend(), but it should not enable the device to signal 541 * wakeup events or change its power state. 542 */ 543static int loongson_drm_pm_freeze(struct device *dev) 544{ 545 struct drm_device *drm_dev = dev_get_drvdata(dev); 546 547 return loongson_drm_suspend(drm_dev, false); 548} 549 550/** 551 * loongson_drm_pm_draw 552 * 553 * @dev pointer to device 554 * 555 * Hibernation-specific, executed after creating a hibernation image OR 556 * if the creation of an image has failed. Also executed after a failing 557 * attempt to restore the contents of main memory from such an image. 558 * Undo the changes made by the preceding @freeze(), so the device can be 559 * operated in the same way as immediately before the call to @freeze(). 560 */ 561static int loongson_drm_pm_thaw(struct device *dev) 562{ 563 struct drm_device *drm_dev = dev_get_drvdata(dev); 564 565 return loongson_drm_resume(drm_dev, false); 566} 567 568#define loongson_drm_pm_poweroff loongson_drm_pm_freeze 569#define loongson_drm_pm_restore loongson_drm_pm_resume 570 571/* 572 * * struct dev_pm_ops - device PM callbacks 573 * 574 *@suspend: Executed before putting the system into a sleep state in which the 575 * contents of main memory are preserved. 576 *@resume: Executed after waking the system up from a sleep state in which the 577 * contents of main memory were preserved. 578 *@freeze: Hibernation-specific, executed before creating a hibernation image. 579 * Analogous to @suspend(), but it should not enable the device to signal 580 * wakeup events or change its power state. The majority of subsystems 581 * (with the notable exception of the PCI bus type) expect the driver-level 582 * @freeze() to save the device settings in memory to be used by @restore() 583 * during the subsequent resume from hibernation. 584 *@thaw: Hibernation-specific, executed after creating a hibernation image OR 585 * if the creation of an image has failed. Also executed after a failing 586 * attempt to restore the contents of main memory from such an image. 587 * Undo the changes made by the preceding @freeze(), so the device can be 588 * operated in the same way as immediately before the call to @freeze(). 589 *@poweroff: Hibernation-specific, executed after saving a hibernation image. 590 * Analogous to @suspend(), but it need not save the device's settings in 591 * memory. 592 *@restore: Hibernation-specific, executed after restoring the contents of main 593 * memory from a hibernation image, analogous to @resume(). 594 */ 595static const struct dev_pm_ops loongson_drm_pm_ops = { 596 .suspend = loongson_drm_pm_suspend, 597 .resume = loongson_drm_pm_resume, 598 .freeze = loongson_drm_pm_freeze, 599 .thaw = loongson_drm_pm_thaw, 600 .poweroff = loongson_drm_pm_poweroff, 601 .restore = loongson_drm_pm_restore, 602}; 603 604/** 605 * loongson_drm_pci_driver -- pci driver structure 606 * 607 * .id_table : must be non-NULL for probe to be called 608 * .probe: New device inserted 609 * .remove: Device removed 610 * .resume: Device suspended 611 * .suspend: Device woken up 612 */ 613static struct pci_driver loongson_drm_pci_driver = { 614 .name = DRIVER_NAME, 615 .id_table = loongson_drm_pci_devices, 616 .probe = loongson_drm_pci_register, 617 .remove = loongson_drm_pci_unregister, 618 .driver.pm = &loongson_drm_pm_ops, 619}; 620 621/** 622 * loongson_drm_pci_init() -- kernel module init function 623 */ 624static int __init loongson_drm_init(void) 625{ 626 int ret; 627 struct pci_dev *pdev = NULL; 628 629 /* If external graphics card exist, use it as default */ 630 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) { 631 if (pdev->vendor == PCI_VENDOR_ID_ATI) 632 return 0; 633 if (pdev->vendor == 0x1a03) /* ASpeed */ 634 return 0; 635 } 636 637 ret = pci_register_driver(&loongson_drm_pci_driver); 638 639 return ret; 640} 641 642/** 643 * loongson_drm_pci_exit() -- kernel module exit function 644 */ 645static void __exit loongson_drm_exit(void) 646{ 647 pci_unregister_driver(&loongson_drm_pci_driver); 648} 649 650module_init(loongson_drm_init); 651module_exit(loongson_drm_exit); 652 653MODULE_AUTHOR("Chen Zhu <zhuchen@loongson.cn>"); 654MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>"); 655MODULE_DESCRIPTION("Loongson LS7A DRM Driver"); 656MODULE_LICENSE("GPL"); 657