18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2018 Loongson Technology Co., Ltd. 38c2ecf20Sopenharmony_ci * Authors: 48c2ecf20Sopenharmony_ci * Chen Zhu <zhuchen@loongson.cn> 58c2ecf20Sopenharmony_ci * Yaling Fang <fangyaling@loongson.cn> 68c2ecf20Sopenharmony_ci * Dandan Zhang <zhangdandan@loongson.cn> 78c2ecf20Sopenharmony_ci * Huacai Chen <chenhc@lemote.com> 88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 98c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 108c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 118c2ecf20Sopenharmony_ci * option) any later version. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/addrspace.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 178c2ecf20Sopenharmony_ci#include <linux/console.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/fs.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/errno.h> 278c2ecf20Sopenharmony_ci#include <linux/init.h> 288c2ecf20Sopenharmony_ci#include <linux/string.h> 298c2ecf20Sopenharmony_ci#include <linux/vga_switcheroo.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 368c2ecf20Sopenharmony_ci#include <loongson.h> 378c2ecf20Sopenharmony_ci#include "loongson_drv.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define DEVICE_NAME "loongson-drm" 408c2ecf20Sopenharmony_ci#define DRIVER_NAME "loongson-drm" 418c2ecf20Sopenharmony_ci#define DRIVER_DESC "Loongson DRM Driver" 428c2ecf20Sopenharmony_ci#define DRIVER_DATE "20201201" 438c2ecf20Sopenharmony_ci#define DRIVER_MAJOR 1 448c2ecf20Sopenharmony_ci#define DRIVER_MINOR 0 458c2ecf20Sopenharmony_ci#define DRIVER_PATCHLEVEL 1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cibool hw_cursor = false; 488c2ecf20Sopenharmony_cimodule_param_named(cursor, hw_cursor, bool, 0600); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cibool poll_connector = false; 518c2ecf20Sopenharmony_cimodule_param_named(poll, poll_connector, bool, 0600); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(loongson_reglock); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * loongson_mode_funcs---basic driver provided mode setting functions 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that 598c2ecf20Sopenharmony_ci * involve drivers. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs loongson_mode_funcs = { 628c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create, 638c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 648c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 658c2ecf20Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/** 698c2ecf20Sopenharmony_ci * loongson_drm_device_init ----init drm device 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * @dev pointer to drm_device structure 728c2ecf20Sopenharmony_ci * @flags start up flag 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * RETURN 758c2ecf20Sopenharmony_ci * drm device init result 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int loongson_drm_device_init(struct drm_device *dev, uint32_t flags) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int ret = 0; 808c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev = dev->dev_private; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ldev->num_crtc = 2; 838c2ecf20Sopenharmony_ci loongson_vbios_init(ldev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /*BAR 0 contains registers */ 868c2ecf20Sopenharmony_ci ldev->mmio_base = pci_resource_start(ldev->dev->pdev, 0); 878c2ecf20Sopenharmony_ci ldev->mmio_size = pci_resource_len(ldev->dev->pdev, 0); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ldev->mmio = pcim_iomap(dev->pdev, 0, 0); 908c2ecf20Sopenharmony_ci if (ldev->mmio == NULL) 918c2ecf20Sopenharmony_ci return -ENOMEM; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci DRM_INFO("ldev->mmio_base = 0x%llx, ldev->mmio_size = 0x%llx\n", 948c2ecf20Sopenharmony_ci ldev->mmio_base, ldev->mmio_size); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!devm_request_mem_region(ldev->dev->dev, ldev->mmio_base, ldev->mmio_size, 978c2ecf20Sopenharmony_ci "loongson_drmfb_mmio")) { 988c2ecf20Sopenharmony_ci DRM_ERROR("Can't reserve mmio registers\n"); 998c2ecf20Sopenharmony_ci return -ENOMEM; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ret = loongson_gpio_init(ldev); 1038c2ecf20Sopenharmony_ci if (ret < 0) 1048c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize dc gpios\n"); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * loongson_modeset_init --- init kernel mode setting 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * @ldev: pointer to loongson_drm_device structure 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * RETURN 1158c2ecf20Sopenharmony_ci * return init result 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ciint loongson_modeset_init(struct loongson_drm_device *ldev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int i, ret; 1208c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 1218c2ecf20Sopenharmony_ci struct drm_connector *connector; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ldev->mode_info[0].mode_config_initialized = true; 1248c2ecf20Sopenharmony_ci ldev->mode_info[1].mode_config_initialized = true; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ldev->dev->mode_config.max_width = LOONGSON_MAX_FB_WIDTH; 1278c2ecf20Sopenharmony_ci ldev->dev->mode_config.max_height = LOONGSON_MAX_FB_HEIGHT; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ldev->dev->mode_config.cursor_width = 32; 1308c2ecf20Sopenharmony_ci ldev->dev->mode_config.cursor_height = 32; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ldev->dev->mode_config.allow_fb_modifiers = true; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = loongson_i2c_init(ldev); 1358c2ecf20Sopenharmony_ci if (ret < 0) { 1368c2ecf20Sopenharmony_ci DRM_ERROR("Failed to initialize i2c\n"); 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci loongson_crtc_init(ldev); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (i=0; i<ldev->num_crtc; i++) { 1438c2ecf20Sopenharmony_ci DRM_DEBUG("loongson drm encoder init\n"); 1448c2ecf20Sopenharmony_ci ldev->mode_info[i].crtc = &ldev->lcrtc[i]; 1458c2ecf20Sopenharmony_ci encoder = loongson_encoder_init(ldev->dev, i); 1468c2ecf20Sopenharmony_ci if (!encoder) { 1478c2ecf20Sopenharmony_ci DRM_ERROR("loongson_encoder_init failed\n"); 1488c2ecf20Sopenharmony_ci return -1; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci ldev->mode_info[i].encoder = to_loongson_encoder(encoder); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci DRM_DEBUG("loongson drm connector init\n"); 1538c2ecf20Sopenharmony_ci connector = loongson_connector_init(ldev->dev, i); 1548c2ecf20Sopenharmony_ci if (!connector) { 1558c2ecf20Sopenharmony_ci DRM_ERROR("loongson_connector_init failed\n"); 1568c2ecf20Sopenharmony_ci return -1; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci ldev->mode_info[i].connector = to_loongson_connector(connector); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 1618c2ecf20Sopenharmony_ci if (poll_connector) 1628c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * loongson_modeset_fini --- deinit kernel mode setting 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * @ldev: pointer to loongson_drm_device structure 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * RETURN 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_civoid loongson_modeset_fini(struct loongson_drm_device *ldev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int loongson_detect_chip(struct loongson_drm_device *ldev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct pci_dev *pdev; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, NULL); 1848c2ecf20Sopenharmony_ci if (pdev) { 1858c2ecf20Sopenharmony_ci ldev->chip = dc_7a1000; 1868c2ecf20Sopenharmony_ci ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a15, NULL); 1878c2ecf20Sopenharmony_ci DRM_INFO("Set LS7A1000 DC device\n"); 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, NULL); 1928c2ecf20Sopenharmony_ci if (pdev) { 1938c2ecf20Sopenharmony_ci ldev->chip = dc_7a2000; 1948c2ecf20Sopenharmony_ci ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a25, NULL); 1958c2ecf20Sopenharmony_ci DRM_INFO("Set LS7A2000 DC device\n"); 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return -1; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * loongson_vga_load - setup chip and create an initial config 2048c2ecf20Sopenharmony_ci * @dev: DRM device 2058c2ecf20Sopenharmony_ci * @flags: startup flags 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * The driver load routine has to do several things: 2088c2ecf20Sopenharmony_ci * - initialize the memory manager 2098c2ecf20Sopenharmony_ci * - allocate initial config memory 2108c2ecf20Sopenharmony_ci * - setup the DRM framebuffer with the allocated memory 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic int loongson_drm_load(struct drm_device *dev, unsigned long flags) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int r, ret, irq; 2158c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32)); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ldev = devm_kzalloc(dev->dev, sizeof(struct loongson_drm_device), GFP_KERNEL); 2208c2ecf20Sopenharmony_ci if (ldev == NULL) 2218c2ecf20Sopenharmony_ci return -ENOMEM; 2228c2ecf20Sopenharmony_ci dev->dev_private = (void *)ldev; 2238c2ecf20Sopenharmony_ci ldev->dev = dev; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = loongson_detect_chip(ldev); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci dev_err(dev->dev, "Fatal error during detect chip: %d\n", ret); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = loongson_drm_device_init(dev, flags); 2308c2ecf20Sopenharmony_ci DRM_DEBUG("end loongson drm device init.\n"); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci drm_mode_config_init(dev); 2338c2ecf20Sopenharmony_ci dev->mode_config.funcs = (void *)&loongson_mode_funcs; 2348c2ecf20Sopenharmony_ci dev->mode_config.preferred_depth = 24; 2358c2ecf20Sopenharmony_ci dev->mode_config.prefer_shadow = 1; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci irq = dev->pdev->irq; 2388c2ecf20Sopenharmony_ci dev_set_drvdata(dev->dev, dev); 2398c2ecf20Sopenharmony_ci pci_set_drvdata(dev->pdev, dev); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci r = loongson_modeset_init(ldev); 2428c2ecf20Sopenharmony_ci if (r) 2438c2ecf20Sopenharmony_ci dev_err(dev->dev, "Fatal error during modeset init: %d\n", r); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci r = drm_irq_install(dev, irq); 2468c2ecf20Sopenharmony_ci if (r) 2478c2ecf20Sopenharmony_ci dev_err(dev->dev, "Fatal error during irq install: %d\n", r); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ldev->inited = true; 2508c2ecf20Sopenharmony_ci drm_mode_config_reset(dev); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci r = drm_vblank_init(dev, ldev->num_crtc); 2538c2ecf20Sopenharmony_ci if (r) 2548c2ecf20Sopenharmony_ci dev_err(dev->dev, "Fatal error during vblank init: %d\n", r); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Make small buffers to store a hardware cursor (double buffered icon updates) */ 2578c2ecf20Sopenharmony_ci ldev->cursor = drm_gem_cma_create(dev, roundup(32*32*4, PAGE_SIZE)); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(dev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci drm_fb_helper_remove_conflicting_framebuffers(NULL, "loongson-drmfb", false); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * loongson_drm_unload--release drm resource 2688c2ecf20Sopenharmony_ci * 2698c2ecf20Sopenharmony_ci * @dev: pointer to drm_device 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic void loongson_drm_unload(struct drm_device *dev) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev = dev->dev_private; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (ldev == NULL) 2778c2ecf20Sopenharmony_ci return; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci loongson_modeset_fini(ldev); 2808c2ecf20Sopenharmony_ci drm_mode_config_cleanup(dev); 2818c2ecf20Sopenharmony_ci dev->dev_private = NULL; 2828c2ecf20Sopenharmony_ci dev_set_drvdata(dev->dev, NULL); 2838c2ecf20Sopenharmony_ci ldev->inited = false; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * loongson_drm_open -Driver callback when a new struct drm_file is opened. 2908c2ecf20Sopenharmony_ci * Useful for setting up driver-private data structures like buffer allocators, 2918c2ecf20Sopenharmony_ci * execution contexts or similar things. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * @dev DRM device 2948c2ecf20Sopenharmony_ci * @file DRM file private date 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * RETURN 2978c2ecf20Sopenharmony_ci * 0 on success, a negative error code on failure, which will be promoted to 2988c2ecf20Sopenharmony_ci * userspace as the result of the open() system call. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic int loongson_drm_open(struct drm_device *dev, struct drm_file *file) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci file->driver_priv = NULL; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci DRM_DEBUG("open: dev=%p, file=%p", dev, file); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(fops); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * loongson_drm_driver - DRM device structure 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * .load: driver callback to complete initialization steps after the driver is registered 3158c2ecf20Sopenharmony_ci * .unload:Reverse the effects of the driver load callback 3168c2ecf20Sopenharmony_ci * .open:Driver callback when a new struct drm_file is opened 3178c2ecf20Sopenharmony_ci * .fops:File operations for the DRM device node. 3188c2ecf20Sopenharmony_ci * .gem_free_object:deconstructor for drm_gem_objects 3198c2ecf20Sopenharmony_ci * .dumb_create:This creates a new dumb buffer in the driver’s backing storage manager 3208c2ecf20Sopenharmony_ci * (GEM, TTM or something else entirely) and returns the resulting buffer handle. 3218c2ecf20Sopenharmony_ci * This handle can then be wrapped up into a framebuffer modeset object 3228c2ecf20Sopenharmony_ci * .dumb_map_offset:Allocate an offset in the drm device node’s address space 3238c2ecf20Sopenharmony_ci * to be able to memory map a dumb buffer 3248c2ecf20Sopenharmony_ci * .dump_destory:This destroys the userspace handle for the given dumb backing storage buffer 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cistatic struct drm_driver loongson_drm_driver = { 3278c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_ATOMIC, 3288c2ecf20Sopenharmony_ci .open = loongson_drm_open, 3298c2ecf20Sopenharmony_ci .fops = &fops, 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci .dumb_create = drm_gem_cma_dumb_create, 3328c2ecf20Sopenharmony_ci .gem_free_object_unlocked = drm_gem_cma_free_object, 3338c2ecf20Sopenharmony_ci .gem_vm_ops = &drm_gem_cma_vm_ops, 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 3368c2ecf20Sopenharmony_ci .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci .gem_prime_import = drm_gem_prime_import, 3398c2ecf20Sopenharmony_ci .gem_prime_export = drm_gem_prime_export, 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 3428c2ecf20Sopenharmony_ci .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 3438c2ecf20Sopenharmony_ci .gem_prime_vmap = drm_gem_cma_prime_vmap, 3448c2ecf20Sopenharmony_ci .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 3458c2ecf20Sopenharmony_ci .gem_prime_mmap = drm_gem_cma_prime_mmap, 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci .irq_handler = loongson_irq_handler, 3488c2ecf20Sopenharmony_ci .irq_preinstall = loongson_irq_preinstall, 3498c2ecf20Sopenharmony_ci .irq_postinstall = loongson_irq_postinstall, 3508c2ecf20Sopenharmony_ci .irq_uninstall = loongson_irq_uninstall, 3518c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 3528c2ecf20Sopenharmony_ci .desc = DRIVER_DESC, 3538c2ecf20Sopenharmony_ci .date = DRIVER_DATE, 3548c2ecf20Sopenharmony_ci .major = DRIVER_MAJOR, 3558c2ecf20Sopenharmony_ci .minor = DRIVER_MINOR, 3568c2ecf20Sopenharmony_ci .patchlevel = DRIVER_PATCHLEVEL, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/** 3608c2ecf20Sopenharmony_ci * loongson_drm_pci_devices -- pci device id info 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * __u32 vendor, device Vendor and device ID or PCI_ANY_ID 3638c2ecf20Sopenharmony_ci * __u32 subvendor, subdevice Subsystem ID's or PCI_ANY_ID 3648c2ecf20Sopenharmony_ci * __u32 class, class_mask (class,subclass,prog-if) triplet 3658c2ecf20Sopenharmony_ci * kernel_ulong_t driver_data Data private to the driver 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic struct pci_device_id loongson_drm_pci_devices[] = { 3688c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1)}, 3698c2ecf20Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2)}, 3708c2ecf20Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, loongson_drm_pci_devices); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * loongson_drm_pci_register -- add pci device 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * @pdev PCI device 3798c2ecf20Sopenharmony_ci * @ent pci device id 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cistatic int loongson_drm_pci_register(struct pci_dev *pdev, 3828c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci int ret; 3868c2ecf20Sopenharmony_ci struct drm_device *dev; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev = drm_dev_alloc(&loongson_drm_driver, &pdev->dev); 3898c2ecf20Sopenharmony_ci if (IS_ERR(dev)) 3908c2ecf20Sopenharmony_ci return PTR_ERR(dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci goto err_free; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dev->pdev = pdev; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci loongson_drm_load(dev, 0x0); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ret = drm_dev_register(dev, 0); 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci goto err_pdev; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(dev, 32); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cierr_pdev: 4098c2ecf20Sopenharmony_ci pci_disable_device(pdev); 4108c2ecf20Sopenharmony_cierr_free: 4118c2ecf20Sopenharmony_ci drm_dev_put(dev); 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/** 4168c2ecf20Sopenharmony_ci * loongson_drm_pci_unregister -- release drm device 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * @pdev PCI device 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cistatic void loongson_drm_pci_unregister(struct pci_dev *pdev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct drm_device *dev = pci_get_drvdata(pdev); 4238c2ecf20Sopenharmony_ci loongson_drm_unload(dev); 4248c2ecf20Sopenharmony_ci drm_dev_put(dev); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/* 4288c2ecf20Sopenharmony_ci * Suspend & resume. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci/* 4318c2ecf20Sopenharmony_ci * loongson_drm_suspend - initiate device suspend 4328c2ecf20Sopenharmony_ci * 4338c2ecf20Sopenharmony_ci * @pdev: drm dev pointer 4348c2ecf20Sopenharmony_ci * @state: suspend state 4358c2ecf20Sopenharmony_ci * 4368c2ecf20Sopenharmony_ci * Puts the hw in the suspend state (all asics). 4378c2ecf20Sopenharmony_ci * Returns 0 for success or an error on failure. 4388c2ecf20Sopenharmony_ci * Called at driver suspend. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ciint loongson_drm_suspend(struct drm_device *dev, bool suspend) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (dev == NULL || dev->dev_private == NULL) 4458c2ecf20Sopenharmony_ci return -ENODEV; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ldev = dev->dev_private; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci drm_kms_helper_poll_disable(dev); 4508c2ecf20Sopenharmony_ci ldev->state = drm_atomic_helper_suspend(dev); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci pci_save_state(dev->pdev); 4538c2ecf20Sopenharmony_ci if (suspend) { 4548c2ecf20Sopenharmony_ci /* Shut down the device */ 4558c2ecf20Sopenharmony_ci pci_disable_device(dev->pdev); 4568c2ecf20Sopenharmony_ci pci_set_power_state(dev->pdev, PCI_D3hot); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci console_lock(); 4608c2ecf20Sopenharmony_ci drm_fb_helper_set_suspend(ldev->dev->fb_helper, 1); 4618c2ecf20Sopenharmony_ci console_unlock(); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * * loongson_drm_resume - initiate device suspend 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * @pdev: drm dev pointer 4708c2ecf20Sopenharmony_ci * @state: suspend state 4718c2ecf20Sopenharmony_ci * 4728c2ecf20Sopenharmony_ci * Puts the hw in the suspend state (all asics). 4738c2ecf20Sopenharmony_ci * Returns 0 for success or an error on failure. 4748c2ecf20Sopenharmony_ci * Called at driver suspend. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ciint loongson_drm_resume(struct drm_device *dev, bool resume) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct loongson_drm_device *ldev = dev->dev_private; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci console_lock(); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (resume) { 4848c2ecf20Sopenharmony_ci pci_set_power_state(dev->pdev, PCI_D0); 4858c2ecf20Sopenharmony_ci pci_restore_state(dev->pdev); 4868c2ecf20Sopenharmony_ci if (pci_enable_device(dev->pdev)) { 4878c2ecf20Sopenharmony_ci console_unlock(); 4888c2ecf20Sopenharmony_ci return -1; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* blat the mode back in */ 4938c2ecf20Sopenharmony_ci drm_atomic_helper_resume(dev, ldev->state); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci drm_kms_helper_poll_enable(dev); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci drm_fb_helper_set_suspend(ldev->dev->fb_helper, 0); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci console_unlock(); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/** 5058c2ecf20Sopenharmony_ci * loongson_drm_pm_suspend 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * @dev pointer to the device 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * Executed before putting the system into a sleep state in which the 5108c2ecf20Sopenharmony_ci * contents of main memory are preserved. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_cistatic int loongson_drm_pm_suspend(struct device *dev) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return loongson_drm_suspend(drm_dev, true); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/** 5208c2ecf20Sopenharmony_ci * loongson_drm_pm_resume 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * @dev pointer to the device 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * Executed after waking the system up from a sleep state in which the 5258c2ecf20Sopenharmony_ci * contents of main memory were preserved. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic int loongson_drm_pm_resume(struct device *dev) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return loongson_drm_resume(drm_dev, true); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci/** 5358c2ecf20Sopenharmony_ci * loongson_drm_pm_freeze 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * @dev pointer to device 5388c2ecf20Sopenharmony_ci * 5398c2ecf20Sopenharmony_ci * Hibernation-specific, executed before creating a hibernation image. 5408c2ecf20Sopenharmony_ci * Analogous to @suspend(), but it should not enable the device to signal 5418c2ecf20Sopenharmony_ci * wakeup events or change its power state. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_cistatic int loongson_drm_pm_freeze(struct device *dev) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return loongson_drm_suspend(drm_dev, false); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/** 5518c2ecf20Sopenharmony_ci * loongson_drm_pm_draw 5528c2ecf20Sopenharmony_ci * 5538c2ecf20Sopenharmony_ci * @dev pointer to device 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * Hibernation-specific, executed after creating a hibernation image OR 5568c2ecf20Sopenharmony_ci * if the creation of an image has failed. Also executed after a failing 5578c2ecf20Sopenharmony_ci * attempt to restore the contents of main memory from such an image. 5588c2ecf20Sopenharmony_ci * Undo the changes made by the preceding @freeze(), so the device can be 5598c2ecf20Sopenharmony_ci * operated in the same way as immediately before the call to @freeze(). 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_cistatic int loongson_drm_pm_thaw(struct device *dev) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(dev); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return loongson_drm_resume(drm_dev, false); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci#define loongson_drm_pm_poweroff loongson_drm_pm_freeze 5698c2ecf20Sopenharmony_ci#define loongson_drm_pm_restore loongson_drm_pm_resume 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* 5728c2ecf20Sopenharmony_ci * * struct dev_pm_ops - device PM callbacks 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci *@suspend: Executed before putting the system into a sleep state in which the 5758c2ecf20Sopenharmony_ci * contents of main memory are preserved. 5768c2ecf20Sopenharmony_ci *@resume: Executed after waking the system up from a sleep state in which the 5778c2ecf20Sopenharmony_ci * contents of main memory were preserved. 5788c2ecf20Sopenharmony_ci *@freeze: Hibernation-specific, executed before creating a hibernation image. 5798c2ecf20Sopenharmony_ci * Analogous to @suspend(), but it should not enable the device to signal 5808c2ecf20Sopenharmony_ci * wakeup events or change its power state. The majority of subsystems 5818c2ecf20Sopenharmony_ci * (with the notable exception of the PCI bus type) expect the driver-level 5828c2ecf20Sopenharmony_ci * @freeze() to save the device settings in memory to be used by @restore() 5838c2ecf20Sopenharmony_ci * during the subsequent resume from hibernation. 5848c2ecf20Sopenharmony_ci *@thaw: Hibernation-specific, executed after creating a hibernation image OR 5858c2ecf20Sopenharmony_ci * if the creation of an image has failed. Also executed after a failing 5868c2ecf20Sopenharmony_ci * attempt to restore the contents of main memory from such an image. 5878c2ecf20Sopenharmony_ci * Undo the changes made by the preceding @freeze(), so the device can be 5888c2ecf20Sopenharmony_ci * operated in the same way as immediately before the call to @freeze(). 5898c2ecf20Sopenharmony_ci *@poweroff: Hibernation-specific, executed after saving a hibernation image. 5908c2ecf20Sopenharmony_ci * Analogous to @suspend(), but it need not save the device's settings in 5918c2ecf20Sopenharmony_ci * memory. 5928c2ecf20Sopenharmony_ci *@restore: Hibernation-specific, executed after restoring the contents of main 5938c2ecf20Sopenharmony_ci * memory from a hibernation image, analogous to @resume(). 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_cistatic const struct dev_pm_ops loongson_drm_pm_ops = { 5968c2ecf20Sopenharmony_ci .suspend = loongson_drm_pm_suspend, 5978c2ecf20Sopenharmony_ci .resume = loongson_drm_pm_resume, 5988c2ecf20Sopenharmony_ci .freeze = loongson_drm_pm_freeze, 5998c2ecf20Sopenharmony_ci .thaw = loongson_drm_pm_thaw, 6008c2ecf20Sopenharmony_ci .poweroff = loongson_drm_pm_poweroff, 6018c2ecf20Sopenharmony_ci .restore = loongson_drm_pm_restore, 6028c2ecf20Sopenharmony_ci}; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/** 6058c2ecf20Sopenharmony_ci * loongson_drm_pci_driver -- pci driver structure 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * .id_table : must be non-NULL for probe to be called 6088c2ecf20Sopenharmony_ci * .probe: New device inserted 6098c2ecf20Sopenharmony_ci * .remove: Device removed 6108c2ecf20Sopenharmony_ci * .resume: Device suspended 6118c2ecf20Sopenharmony_ci * .suspend: Device woken up 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_cistatic struct pci_driver loongson_drm_pci_driver = { 6148c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 6158c2ecf20Sopenharmony_ci .id_table = loongson_drm_pci_devices, 6168c2ecf20Sopenharmony_ci .probe = loongson_drm_pci_register, 6178c2ecf20Sopenharmony_ci .remove = loongson_drm_pci_unregister, 6188c2ecf20Sopenharmony_ci .driver.pm = &loongson_drm_pm_ops, 6198c2ecf20Sopenharmony_ci}; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/** 6228c2ecf20Sopenharmony_ci * loongson_drm_pci_init() -- kernel module init function 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_cistatic int __init loongson_drm_init(void) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int ret; 6278c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* If external graphics card exist, use it as default */ 6308c2ecf20Sopenharmony_ci while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) { 6318c2ecf20Sopenharmony_ci if (pdev->vendor == PCI_VENDOR_ID_ATI) 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci if (pdev->vendor == 0x1a03) /* ASpeed */ 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ret = pci_register_driver(&loongson_drm_pci_driver); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/** 6438c2ecf20Sopenharmony_ci * loongson_drm_pci_exit() -- kernel module exit function 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_cistatic void __exit loongson_drm_exit(void) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci pci_unregister_driver(&loongson_drm_pci_driver); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cimodule_init(loongson_drm_init); 6518c2ecf20Sopenharmony_cimodule_exit(loongson_drm_exit); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chen Zhu <zhuchen@loongson.cn>"); 6548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>"); 6558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Loongson LS7A DRM Driver"); 6568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 657