1*4f2a8f58SJoel Stanley // SPDX-License-Identifier: GPL-2.0+ 2*4f2a8f58SJoel Stanley // Copyright 2018 IBM Corporation 3*4f2a8f58SJoel Stanley 4*4f2a8f58SJoel Stanley #include <linux/clk.h> 5*4f2a8f58SJoel Stanley #include <linux/dma-mapping.h> 6*4f2a8f58SJoel Stanley #include <linux/irq.h> 7*4f2a8f58SJoel Stanley #include <linux/mfd/syscon.h> 8*4f2a8f58SJoel Stanley #include <linux/module.h> 9*4f2a8f58SJoel Stanley #include <linux/of.h> 10*4f2a8f58SJoel Stanley #include <linux/of_reserved_mem.h> 11*4f2a8f58SJoel Stanley #include <linux/platform_device.h> 12*4f2a8f58SJoel Stanley #include <linux/regmap.h> 13*4f2a8f58SJoel Stanley #include <linux/reset.h> 14*4f2a8f58SJoel Stanley 15*4f2a8f58SJoel Stanley #include <drm/drm_atomic_helper.h> 16*4f2a8f58SJoel Stanley #include <drm/drm_crtc_helper.h> 17*4f2a8f58SJoel Stanley #include <drm/drm_device.h> 18*4f2a8f58SJoel Stanley #include <drm/drm_fb_cma_helper.h> 19*4f2a8f58SJoel Stanley #include <drm/drm_fb_helper.h> 20*4f2a8f58SJoel Stanley #include <drm/drm_gem_cma_helper.h> 21*4f2a8f58SJoel Stanley #include <drm/drm_gem_framebuffer_helper.h> 22*4f2a8f58SJoel Stanley #include <drm/drm_probe_helper.h> 23*4f2a8f58SJoel Stanley #include <drm/drm_simple_kms_helper.h> 24*4f2a8f58SJoel Stanley #include <drm/drm_vblank.h> 25*4f2a8f58SJoel Stanley #include <drm/drm_drv.h> 26*4f2a8f58SJoel Stanley 27*4f2a8f58SJoel Stanley #include "aspeed_gfx.h" 28*4f2a8f58SJoel Stanley 29*4f2a8f58SJoel Stanley /** 30*4f2a8f58SJoel Stanley * DOC: ASPEED GFX Driver 31*4f2a8f58SJoel Stanley * 32*4f2a8f58SJoel Stanley * This driver is for the ASPEED BMC SoC's 'GFX' display hardware, also called 33*4f2a8f58SJoel Stanley * the 'SOC Display Controller' in the datasheet. This driver runs on the ARM 34*4f2a8f58SJoel Stanley * based BMC systems, unlike the ast driver which runs on a host CPU and is for 35*4f2a8f58SJoel Stanley * a PCIe graphics device. 36*4f2a8f58SJoel Stanley * 37*4f2a8f58SJoel Stanley * The AST2500 supports a total of 3 output paths: 38*4f2a8f58SJoel Stanley * 39*4f2a8f58SJoel Stanley * 1. VGA output, the output target can choose either or both to the DAC 40*4f2a8f58SJoel Stanley * or DVO interface. 41*4f2a8f58SJoel Stanley * 42*4f2a8f58SJoel Stanley * 2. Graphics CRT output, the output target can choose either or both to 43*4f2a8f58SJoel Stanley * the DAC or DVO interface. 44*4f2a8f58SJoel Stanley * 45*4f2a8f58SJoel Stanley * 3. Video input from DVO, the video input can be used for video engine 46*4f2a8f58SJoel Stanley * capture or DAC display output. 47*4f2a8f58SJoel Stanley * 48*4f2a8f58SJoel Stanley * Output options are selected in SCU2C. 49*4f2a8f58SJoel Stanley * 50*4f2a8f58SJoel Stanley * The "VGA mode" device is the PCI attached controller. The "Graphics CRT" 51*4f2a8f58SJoel Stanley * is the ARM's internal display controller. 52*4f2a8f58SJoel Stanley * 53*4f2a8f58SJoel Stanley * The driver only supports a simple configuration consisting of a 40MHz 54*4f2a8f58SJoel Stanley * pixel clock, fixed by hardware limitations, and the VGA output path. 55*4f2a8f58SJoel Stanley * 56*4f2a8f58SJoel Stanley * The driver was written with the 'AST2500 Software Programming Guide' v17, 57*4f2a8f58SJoel Stanley * which is available under NDA from ASPEED. 58*4f2a8f58SJoel Stanley */ 59*4f2a8f58SJoel Stanley 60*4f2a8f58SJoel Stanley static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = { 61*4f2a8f58SJoel Stanley .fb_create = drm_gem_fb_create, 62*4f2a8f58SJoel Stanley .atomic_check = drm_atomic_helper_check, 63*4f2a8f58SJoel Stanley .atomic_commit = drm_atomic_helper_commit, 64*4f2a8f58SJoel Stanley }; 65*4f2a8f58SJoel Stanley 66*4f2a8f58SJoel Stanley static void aspeed_gfx_setup_mode_config(struct drm_device *drm) 67*4f2a8f58SJoel Stanley { 68*4f2a8f58SJoel Stanley drm_mode_config_init(drm); 69*4f2a8f58SJoel Stanley 70*4f2a8f58SJoel Stanley drm->mode_config.min_width = 0; 71*4f2a8f58SJoel Stanley drm->mode_config.min_height = 0; 72*4f2a8f58SJoel Stanley drm->mode_config.max_width = 800; 73*4f2a8f58SJoel Stanley drm->mode_config.max_height = 600; 74*4f2a8f58SJoel Stanley drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs; 75*4f2a8f58SJoel Stanley } 76*4f2a8f58SJoel Stanley 77*4f2a8f58SJoel Stanley static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data) 78*4f2a8f58SJoel Stanley { 79*4f2a8f58SJoel Stanley struct drm_device *drm = data; 80*4f2a8f58SJoel Stanley struct aspeed_gfx *priv = drm->dev_private; 81*4f2a8f58SJoel Stanley u32 reg; 82*4f2a8f58SJoel Stanley 83*4f2a8f58SJoel Stanley reg = readl(priv->base + CRT_CTRL1); 84*4f2a8f58SJoel Stanley 85*4f2a8f58SJoel Stanley if (reg & CRT_CTRL_VERTICAL_INTR_STS) { 86*4f2a8f58SJoel Stanley drm_crtc_handle_vblank(&priv->pipe.crtc); 87*4f2a8f58SJoel Stanley writel(reg, priv->base + CRT_CTRL1); 88*4f2a8f58SJoel Stanley return IRQ_HANDLED; 89*4f2a8f58SJoel Stanley } 90*4f2a8f58SJoel Stanley 91*4f2a8f58SJoel Stanley return IRQ_NONE; 92*4f2a8f58SJoel Stanley } 93*4f2a8f58SJoel Stanley 94*4f2a8f58SJoel Stanley 95*4f2a8f58SJoel Stanley 96*4f2a8f58SJoel Stanley static int aspeed_gfx_load(struct drm_device *drm) 97*4f2a8f58SJoel Stanley { 98*4f2a8f58SJoel Stanley struct platform_device *pdev = to_platform_device(drm->dev); 99*4f2a8f58SJoel Stanley struct aspeed_gfx *priv; 100*4f2a8f58SJoel Stanley struct resource *res; 101*4f2a8f58SJoel Stanley int ret; 102*4f2a8f58SJoel Stanley 103*4f2a8f58SJoel Stanley priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 104*4f2a8f58SJoel Stanley if (!priv) 105*4f2a8f58SJoel Stanley return -ENOMEM; 106*4f2a8f58SJoel Stanley drm->dev_private = priv; 107*4f2a8f58SJoel Stanley 108*4f2a8f58SJoel Stanley res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 109*4f2a8f58SJoel Stanley priv->base = devm_ioremap_resource(drm->dev, res); 110*4f2a8f58SJoel Stanley if (IS_ERR(priv->base)) 111*4f2a8f58SJoel Stanley return PTR_ERR(priv->base); 112*4f2a8f58SJoel Stanley 113*4f2a8f58SJoel Stanley priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu"); 114*4f2a8f58SJoel Stanley if (IS_ERR(priv->scu)) { 115*4f2a8f58SJoel Stanley dev_err(&pdev->dev, "failed to find SCU regmap\n"); 116*4f2a8f58SJoel Stanley return PTR_ERR(priv->scu); 117*4f2a8f58SJoel Stanley } 118*4f2a8f58SJoel Stanley 119*4f2a8f58SJoel Stanley ret = of_reserved_mem_device_init(drm->dev); 120*4f2a8f58SJoel Stanley if (ret) { 121*4f2a8f58SJoel Stanley dev_err(&pdev->dev, 122*4f2a8f58SJoel Stanley "failed to initialize reserved mem: %d\n", ret); 123*4f2a8f58SJoel Stanley return ret; 124*4f2a8f58SJoel Stanley } 125*4f2a8f58SJoel Stanley 126*4f2a8f58SJoel Stanley ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); 127*4f2a8f58SJoel Stanley if (ret) { 128*4f2a8f58SJoel Stanley dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret); 129*4f2a8f58SJoel Stanley return ret; 130*4f2a8f58SJoel Stanley } 131*4f2a8f58SJoel Stanley 132*4f2a8f58SJoel Stanley priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); 133*4f2a8f58SJoel Stanley if (IS_ERR(priv->rst)) { 134*4f2a8f58SJoel Stanley dev_err(&pdev->dev, 135*4f2a8f58SJoel Stanley "missing or invalid reset controller device tree entry"); 136*4f2a8f58SJoel Stanley return PTR_ERR(priv->rst); 137*4f2a8f58SJoel Stanley } 138*4f2a8f58SJoel Stanley reset_control_deassert(priv->rst); 139*4f2a8f58SJoel Stanley 140*4f2a8f58SJoel Stanley priv->clk = devm_clk_get(drm->dev, NULL); 141*4f2a8f58SJoel Stanley if (IS_ERR(priv->clk)) { 142*4f2a8f58SJoel Stanley dev_err(&pdev->dev, 143*4f2a8f58SJoel Stanley "missing or invalid clk device tree entry"); 144*4f2a8f58SJoel Stanley return PTR_ERR(priv->clk); 145*4f2a8f58SJoel Stanley } 146*4f2a8f58SJoel Stanley clk_prepare_enable(priv->clk); 147*4f2a8f58SJoel Stanley 148*4f2a8f58SJoel Stanley /* Sanitize control registers */ 149*4f2a8f58SJoel Stanley writel(0, priv->base + CRT_CTRL1); 150*4f2a8f58SJoel Stanley writel(0, priv->base + CRT_CTRL2); 151*4f2a8f58SJoel Stanley 152*4f2a8f58SJoel Stanley aspeed_gfx_setup_mode_config(drm); 153*4f2a8f58SJoel Stanley 154*4f2a8f58SJoel Stanley ret = drm_vblank_init(drm, 1); 155*4f2a8f58SJoel Stanley if (ret < 0) { 156*4f2a8f58SJoel Stanley dev_err(drm->dev, "Failed to initialise vblank\n"); 157*4f2a8f58SJoel Stanley return ret; 158*4f2a8f58SJoel Stanley } 159*4f2a8f58SJoel Stanley 160*4f2a8f58SJoel Stanley ret = aspeed_gfx_create_output(drm); 161*4f2a8f58SJoel Stanley if (ret < 0) { 162*4f2a8f58SJoel Stanley dev_err(drm->dev, "Failed to create outputs\n"); 163*4f2a8f58SJoel Stanley return ret; 164*4f2a8f58SJoel Stanley } 165*4f2a8f58SJoel Stanley 166*4f2a8f58SJoel Stanley ret = aspeed_gfx_create_pipe(drm); 167*4f2a8f58SJoel Stanley if (ret < 0) { 168*4f2a8f58SJoel Stanley dev_err(drm->dev, "Cannot setup simple display pipe\n"); 169*4f2a8f58SJoel Stanley return ret; 170*4f2a8f58SJoel Stanley } 171*4f2a8f58SJoel Stanley 172*4f2a8f58SJoel Stanley ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0), 173*4f2a8f58SJoel Stanley aspeed_gfx_irq_handler, 0, "aspeed gfx", drm); 174*4f2a8f58SJoel Stanley if (ret < 0) { 175*4f2a8f58SJoel Stanley dev_err(drm->dev, "Failed to install IRQ handler\n"); 176*4f2a8f58SJoel Stanley return ret; 177*4f2a8f58SJoel Stanley } 178*4f2a8f58SJoel Stanley 179*4f2a8f58SJoel Stanley drm_mode_config_reset(drm); 180*4f2a8f58SJoel Stanley 181*4f2a8f58SJoel Stanley drm_fbdev_generic_setup(drm, 32); 182*4f2a8f58SJoel Stanley 183*4f2a8f58SJoel Stanley return 0; 184*4f2a8f58SJoel Stanley } 185*4f2a8f58SJoel Stanley 186*4f2a8f58SJoel Stanley static void aspeed_gfx_unload(struct drm_device *drm) 187*4f2a8f58SJoel Stanley { 188*4f2a8f58SJoel Stanley drm_kms_helper_poll_fini(drm); 189*4f2a8f58SJoel Stanley drm_mode_config_cleanup(drm); 190*4f2a8f58SJoel Stanley 191*4f2a8f58SJoel Stanley drm->dev_private = NULL; 192*4f2a8f58SJoel Stanley } 193*4f2a8f58SJoel Stanley 194*4f2a8f58SJoel Stanley DEFINE_DRM_GEM_CMA_FOPS(fops); 195*4f2a8f58SJoel Stanley 196*4f2a8f58SJoel Stanley static struct drm_driver aspeed_gfx_driver = { 197*4f2a8f58SJoel Stanley .driver_features = DRIVER_GEM | DRIVER_MODESET | 198*4f2a8f58SJoel Stanley DRIVER_PRIME | DRIVER_ATOMIC, 199*4f2a8f58SJoel Stanley .gem_create_object = drm_cma_gem_create_object_default_funcs, 200*4f2a8f58SJoel Stanley .dumb_create = drm_gem_cma_dumb_create, 201*4f2a8f58SJoel Stanley .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 202*4f2a8f58SJoel Stanley .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 203*4f2a8f58SJoel Stanley .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 204*4f2a8f58SJoel Stanley .gem_prime_mmap = drm_gem_prime_mmap, 205*4f2a8f58SJoel Stanley .fops = &fops, 206*4f2a8f58SJoel Stanley .name = "aspeed-gfx-drm", 207*4f2a8f58SJoel Stanley .desc = "ASPEED GFX DRM", 208*4f2a8f58SJoel Stanley .date = "20180319", 209*4f2a8f58SJoel Stanley .major = 1, 210*4f2a8f58SJoel Stanley .minor = 0, 211*4f2a8f58SJoel Stanley }; 212*4f2a8f58SJoel Stanley 213*4f2a8f58SJoel Stanley static const struct of_device_id aspeed_gfx_match[] = { 214*4f2a8f58SJoel Stanley { .compatible = "aspeed,ast2500-gfx" }, 215*4f2a8f58SJoel Stanley { } 216*4f2a8f58SJoel Stanley }; 217*4f2a8f58SJoel Stanley 218*4f2a8f58SJoel Stanley static int aspeed_gfx_probe(struct platform_device *pdev) 219*4f2a8f58SJoel Stanley { 220*4f2a8f58SJoel Stanley struct drm_device *drm; 221*4f2a8f58SJoel Stanley int ret; 222*4f2a8f58SJoel Stanley 223*4f2a8f58SJoel Stanley drm = drm_dev_alloc(&aspeed_gfx_driver, &pdev->dev); 224*4f2a8f58SJoel Stanley if (IS_ERR(drm)) 225*4f2a8f58SJoel Stanley return PTR_ERR(drm); 226*4f2a8f58SJoel Stanley 227*4f2a8f58SJoel Stanley ret = aspeed_gfx_load(drm); 228*4f2a8f58SJoel Stanley if (ret) 229*4f2a8f58SJoel Stanley goto err_free; 230*4f2a8f58SJoel Stanley 231*4f2a8f58SJoel Stanley ret = drm_dev_register(drm, 0); 232*4f2a8f58SJoel Stanley if (ret) 233*4f2a8f58SJoel Stanley goto err_unload; 234*4f2a8f58SJoel Stanley 235*4f2a8f58SJoel Stanley return 0; 236*4f2a8f58SJoel Stanley 237*4f2a8f58SJoel Stanley err_unload: 238*4f2a8f58SJoel Stanley aspeed_gfx_unload(drm); 239*4f2a8f58SJoel Stanley err_free: 240*4f2a8f58SJoel Stanley drm_dev_put(drm); 241*4f2a8f58SJoel Stanley 242*4f2a8f58SJoel Stanley return ret; 243*4f2a8f58SJoel Stanley } 244*4f2a8f58SJoel Stanley 245*4f2a8f58SJoel Stanley static int aspeed_gfx_remove(struct platform_device *pdev) 246*4f2a8f58SJoel Stanley { 247*4f2a8f58SJoel Stanley struct drm_device *drm = platform_get_drvdata(pdev); 248*4f2a8f58SJoel Stanley 249*4f2a8f58SJoel Stanley drm_dev_unregister(drm); 250*4f2a8f58SJoel Stanley aspeed_gfx_unload(drm); 251*4f2a8f58SJoel Stanley drm_dev_put(drm); 252*4f2a8f58SJoel Stanley 253*4f2a8f58SJoel Stanley return 0; 254*4f2a8f58SJoel Stanley } 255*4f2a8f58SJoel Stanley 256*4f2a8f58SJoel Stanley static struct platform_driver aspeed_gfx_platform_driver = { 257*4f2a8f58SJoel Stanley .probe = aspeed_gfx_probe, 258*4f2a8f58SJoel Stanley .remove = aspeed_gfx_remove, 259*4f2a8f58SJoel Stanley .driver = { 260*4f2a8f58SJoel Stanley .name = "aspeed_gfx", 261*4f2a8f58SJoel Stanley .of_match_table = aspeed_gfx_match, 262*4f2a8f58SJoel Stanley }, 263*4f2a8f58SJoel Stanley }; 264*4f2a8f58SJoel Stanley 265*4f2a8f58SJoel Stanley module_platform_driver(aspeed_gfx_platform_driver); 266*4f2a8f58SJoel Stanley 267*4f2a8f58SJoel Stanley MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); 268*4f2a8f58SJoel Stanley MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver"); 269*4f2a8f58SJoel Stanley MODULE_LICENSE("GPL"); 270