Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 // Copyright 2018 IBM Corporation
0003 
0004 #include <linux/clk.h>
0005 #include <linux/dma-mapping.h>
0006 #include <linux/irq.h>
0007 #include <linux/mfd/syscon.h>
0008 #include <linux/module.h>
0009 #include <linux/of.h>
0010 #include <linux/of_device.h>
0011 #include <linux/of_reserved_mem.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/regmap.h>
0014 #include <linux/reset.h>
0015 
0016 #include <drm/drm_atomic_helper.h>
0017 #include <drm/drm_crtc_helper.h>
0018 #include <drm/drm_device.h>
0019 #include <drm/drm_fb_cma_helper.h>
0020 #include <drm/drm_fb_helper.h>
0021 #include <drm/drm_gem_cma_helper.h>
0022 #include <drm/drm_gem_framebuffer_helper.h>
0023 #include <drm/drm_module.h>
0024 #include <drm/drm_probe_helper.h>
0025 #include <drm/drm_simple_kms_helper.h>
0026 #include <drm/drm_vblank.h>
0027 #include <drm/drm_drv.h>
0028 
0029 #include "aspeed_gfx.h"
0030 
0031 /**
0032  * DOC: ASPEED GFX Driver
0033  *
0034  * This driver is for the ASPEED BMC SoC's 'GFX' display hardware, also called
0035  * the 'SOC Display Controller' in the datasheet. This driver runs on the ARM
0036  * based BMC systems, unlike the ast driver which runs on a host CPU and is for
0037  * a PCIe graphics device.
0038  *
0039  * The AST2500 supports a total of 3 output paths:
0040  *
0041  *   1. VGA output, the output target can choose either or both to the DAC
0042  *   or DVO interface.
0043  *
0044  *   2. Graphics CRT output, the output target can choose either or both to
0045  *   the DAC or DVO interface.
0046  *
0047  *   3. Video input from DVO, the video input can be used for video engine
0048  *   capture or DAC display output.
0049  *
0050  * Output options are selected in SCU2C.
0051  *
0052  * The "VGA mode" device is the PCI attached controller. The "Graphics CRT"
0053  * is the ARM's internal display controller.
0054  *
0055  * The driver only supports a simple configuration consisting of a 40MHz
0056  * pixel clock, fixed by hardware limitations, and the VGA output path.
0057  *
0058  * The driver was written with the 'AST2500 Software Programming Guide' v17,
0059  * which is available under NDA from ASPEED.
0060  */
0061 
0062 struct aspeed_gfx_config {
0063     u32 dac_reg;        /* DAC register in SCU */
0064     u32 int_clear_reg;  /* Interrupt clear register */
0065     u32 vga_scratch_reg;    /* VGA scratch register in SCU */
0066     u32 throd_val;      /* Default Threshold Seting */
0067     u32 scan_line_max;  /* Max memory size of one scan line */
0068 };
0069 
0070 static const struct aspeed_gfx_config ast2400_config = {
0071     .dac_reg = 0x2c,
0072     .int_clear_reg = 0x60,
0073     .vga_scratch_reg = 0x50,
0074     .throd_val = CRT_THROD_LOW(0x1e) | CRT_THROD_HIGH(0x12),
0075     .scan_line_max = 64,
0076 };
0077 
0078 static const struct aspeed_gfx_config ast2500_config = {
0079     .dac_reg = 0x2c,
0080     .int_clear_reg = 0x60,
0081     .vga_scratch_reg = 0x50,
0082     .throd_val = CRT_THROD_LOW(0x24) | CRT_THROD_HIGH(0x3c),
0083     .scan_line_max = 128,
0084 };
0085 
0086 static const struct aspeed_gfx_config ast2600_config = {
0087     .dac_reg = 0xc0,
0088     .int_clear_reg = 0x68,
0089     .vga_scratch_reg = 0x50,
0090     .throd_val = CRT_THROD_LOW(0x50) | CRT_THROD_HIGH(0x70),
0091     .scan_line_max = 128,
0092 };
0093 
0094 static const struct of_device_id aspeed_gfx_match[] = {
0095     { .compatible = "aspeed,ast2400-gfx", .data = &ast2400_config },
0096     { .compatible = "aspeed,ast2500-gfx", .data = &ast2500_config },
0097     { .compatible = "aspeed,ast2600-gfx", .data = &ast2600_config },
0098     { },
0099 };
0100 MODULE_DEVICE_TABLE(of, aspeed_gfx_match);
0101 
0102 static const struct drm_mode_config_funcs aspeed_gfx_mode_config_funcs = {
0103     .fb_create      = drm_gem_fb_create,
0104     .atomic_check       = drm_atomic_helper_check,
0105     .atomic_commit      = drm_atomic_helper_commit,
0106 };
0107 
0108 static int aspeed_gfx_setup_mode_config(struct drm_device *drm)
0109 {
0110     int ret;
0111 
0112     ret = drmm_mode_config_init(drm);
0113     if (ret)
0114         return ret;
0115 
0116     drm->mode_config.min_width = 0;
0117     drm->mode_config.min_height = 0;
0118     drm->mode_config.max_width = 800;
0119     drm->mode_config.max_height = 600;
0120     drm->mode_config.funcs = &aspeed_gfx_mode_config_funcs;
0121 
0122     return ret;
0123 }
0124 
0125 static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
0126 {
0127     struct drm_device *drm = data;
0128     struct aspeed_gfx *priv = to_aspeed_gfx(drm);
0129     u32 reg;
0130 
0131     reg = readl(priv->base + CRT_CTRL1);
0132 
0133     if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
0134         drm_crtc_handle_vblank(&priv->pipe.crtc);
0135         writel(reg, priv->base + priv->int_clr_reg);
0136         return IRQ_HANDLED;
0137     }
0138 
0139     return IRQ_NONE;
0140 }
0141 
0142 static int aspeed_gfx_load(struct drm_device *drm)
0143 {
0144     struct platform_device *pdev = to_platform_device(drm->dev);
0145     struct aspeed_gfx *priv = to_aspeed_gfx(drm);
0146     struct device_node *np = pdev->dev.of_node;
0147     const struct aspeed_gfx_config *config;
0148     const struct of_device_id *match;
0149     struct resource *res;
0150     int ret;
0151 
0152     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0153     priv->base = devm_ioremap_resource(drm->dev, res);
0154     if (IS_ERR(priv->base))
0155         return PTR_ERR(priv->base);
0156 
0157     match = of_match_device(aspeed_gfx_match, &pdev->dev);
0158     if (!match)
0159         return -EINVAL;
0160     config = match->data;
0161 
0162     priv->dac_reg = config->dac_reg;
0163     priv->int_clr_reg = config->int_clear_reg;
0164     priv->vga_scratch_reg = config->vga_scratch_reg;
0165     priv->throd_val = config->throd_val;
0166     priv->scan_line_max = config->scan_line_max;
0167 
0168     priv->scu = syscon_regmap_lookup_by_phandle(np, "syscon");
0169     if (IS_ERR(priv->scu)) {
0170         priv->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
0171         if (IS_ERR(priv->scu)) {
0172             dev_err(&pdev->dev, "failed to find SCU regmap\n");
0173             return PTR_ERR(priv->scu);
0174         }
0175     }
0176 
0177     ret = of_reserved_mem_device_init(drm->dev);
0178     if (ret) {
0179         dev_err(&pdev->dev,
0180             "failed to initialize reserved mem: %d\n", ret);
0181         return ret;
0182     }
0183 
0184     ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
0185     if (ret) {
0186         dev_err(&pdev->dev, "failed to set DMA mask: %d\n", ret);
0187         return ret;
0188     }
0189 
0190     priv->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
0191     if (IS_ERR(priv->rst)) {
0192         dev_err(&pdev->dev,
0193             "missing or invalid reset controller device tree entry");
0194         return PTR_ERR(priv->rst);
0195     }
0196     reset_control_deassert(priv->rst);
0197 
0198     priv->clk = devm_clk_get(drm->dev, NULL);
0199     if (IS_ERR(priv->clk)) {
0200         dev_err(&pdev->dev,
0201             "missing or invalid clk device tree entry");
0202         return PTR_ERR(priv->clk);
0203     }
0204     clk_prepare_enable(priv->clk);
0205 
0206     /* Sanitize control registers */
0207     writel(0, priv->base + CRT_CTRL1);
0208     writel(0, priv->base + CRT_CTRL2);
0209 
0210     ret = aspeed_gfx_setup_mode_config(drm);
0211     if (ret < 0)
0212         return ret;
0213 
0214     ret = drm_vblank_init(drm, 1);
0215     if (ret < 0) {
0216         dev_err(drm->dev, "Failed to initialise vblank\n");
0217         return ret;
0218     }
0219 
0220     ret = aspeed_gfx_create_output(drm);
0221     if (ret < 0) {
0222         dev_err(drm->dev, "Failed to create outputs\n");
0223         return ret;
0224     }
0225 
0226     ret = aspeed_gfx_create_pipe(drm);
0227     if (ret < 0) {
0228         dev_err(drm->dev, "Cannot setup simple display pipe\n");
0229         return ret;
0230     }
0231 
0232     ret = devm_request_irq(drm->dev, platform_get_irq(pdev, 0),
0233                    aspeed_gfx_irq_handler, 0, "aspeed gfx", drm);
0234     if (ret < 0) {
0235         dev_err(drm->dev, "Failed to install IRQ handler\n");
0236         return ret;
0237     }
0238 
0239     drm_mode_config_reset(drm);
0240 
0241     return 0;
0242 }
0243 
0244 static void aspeed_gfx_unload(struct drm_device *drm)
0245 {
0246     drm_kms_helper_poll_fini(drm);
0247 }
0248 
0249 DEFINE_DRM_GEM_CMA_FOPS(fops);
0250 
0251 static const struct drm_driver aspeed_gfx_driver = {
0252     .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
0253     DRM_GEM_CMA_DRIVER_OPS,
0254     .fops = &fops,
0255     .name = "aspeed-gfx-drm",
0256     .desc = "ASPEED GFX DRM",
0257     .date = "20180319",
0258     .major = 1,
0259     .minor = 0,
0260 };
0261 
0262 static ssize_t dac_mux_store(struct device *dev, struct device_attribute *attr,
0263                  const char *buf, size_t count)
0264 {
0265     struct aspeed_gfx *priv = dev_get_drvdata(dev);
0266     u32 val;
0267     int rc;
0268 
0269     rc = kstrtou32(buf, 0, &val);
0270     if (rc)
0271         return rc;
0272 
0273     if (val > 3)
0274         return -EINVAL;
0275 
0276     rc = regmap_update_bits(priv->scu, priv->dac_reg, 0x30000, val << 16);
0277     if (rc < 0)
0278         return 0;
0279 
0280     return count;
0281 }
0282 
0283 static ssize_t dac_mux_show(struct device *dev, struct device_attribute *attr, char *buf)
0284 {
0285     struct aspeed_gfx *priv = dev_get_drvdata(dev);
0286     u32 reg;
0287     int rc;
0288 
0289     rc = regmap_read(priv->scu, priv->dac_reg, &reg);
0290     if (rc)
0291         return rc;
0292 
0293     return sprintf(buf, "%u\n", (reg >> 16) & 0x3);
0294 }
0295 static DEVICE_ATTR_RW(dac_mux);
0296 
0297 static ssize_t
0298 vga_pw_show(struct device *dev, struct device_attribute *attr, char *buf)
0299 {
0300     struct aspeed_gfx *priv = dev_get_drvdata(dev);
0301     u32 reg;
0302     int rc;
0303 
0304     rc = regmap_read(priv->scu, priv->vga_scratch_reg, &reg);
0305     if (rc)
0306         return rc;
0307 
0308     return sprintf(buf, "%u\n", reg);
0309 }
0310 static DEVICE_ATTR_RO(vga_pw);
0311 
0312 static struct attribute *aspeed_sysfs_entries[] = {
0313     &dev_attr_vga_pw.attr,
0314     &dev_attr_dac_mux.attr,
0315     NULL,
0316 };
0317 
0318 static struct attribute_group aspeed_sysfs_attr_group = {
0319     .attrs = aspeed_sysfs_entries,
0320 };
0321 
0322 static int aspeed_gfx_probe(struct platform_device *pdev)
0323 {
0324     struct aspeed_gfx *priv;
0325     int ret;
0326 
0327     priv = devm_drm_dev_alloc(&pdev->dev, &aspeed_gfx_driver,
0328                   struct aspeed_gfx, drm);
0329     if (IS_ERR(priv))
0330         return PTR_ERR(priv);
0331 
0332     ret = aspeed_gfx_load(&priv->drm);
0333     if (ret)
0334         return ret;
0335 
0336     platform_set_drvdata(pdev, priv);
0337 
0338     ret = sysfs_create_group(&pdev->dev.kobj, &aspeed_sysfs_attr_group);
0339     if (ret)
0340         return ret;
0341 
0342     ret = drm_dev_register(&priv->drm, 0);
0343     if (ret)
0344         goto err_unload;
0345 
0346     drm_fbdev_generic_setup(&priv->drm, 32);
0347     return 0;
0348 
0349 err_unload:
0350     sysfs_remove_group(&pdev->dev.kobj, &aspeed_sysfs_attr_group);
0351     aspeed_gfx_unload(&priv->drm);
0352 
0353     return ret;
0354 }
0355 
0356 static int aspeed_gfx_remove(struct platform_device *pdev)
0357 {
0358     struct drm_device *drm = platform_get_drvdata(pdev);
0359 
0360     sysfs_remove_group(&pdev->dev.kobj, &aspeed_sysfs_attr_group);
0361     drm_dev_unregister(drm);
0362     aspeed_gfx_unload(drm);
0363 
0364     return 0;
0365 }
0366 
0367 static struct platform_driver aspeed_gfx_platform_driver = {
0368     .probe      = aspeed_gfx_probe,
0369     .remove     = aspeed_gfx_remove,
0370     .driver = {
0371         .name = "aspeed_gfx",
0372         .of_match_table = aspeed_gfx_match,
0373     },
0374 };
0375 
0376 drm_module_platform_driver(aspeed_gfx_platform_driver);
0377 
0378 MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
0379 MODULE_DESCRIPTION("ASPEED BMC DRM/KMS driver");
0380 MODULE_LICENSE("GPL");