Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2016 BayLibre, SAS
0004  * Author: Neil Armstrong <narmstrong@baylibre.com>
0005  * Copyright (C) 2014 Endless Mobile
0006  *
0007  * Written by:
0008  *     Jasper St. Pierre <jstpierre@mecheye.net>
0009  */
0010 
0011 #include <linux/component.h>
0012 #include <linux/module.h>
0013 #include <linux/of_graph.h>
0014 #include <linux/sys_soc.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/soc/amlogic/meson-canvas.h>
0017 
0018 #include <drm/drm_aperture.h>
0019 #include <drm/drm_atomic_helper.h>
0020 #include <drm/drm_drv.h>
0021 #include <drm/drm_fb_helper.h>
0022 #include <drm/drm_gem_cma_helper.h>
0023 #include <drm/drm_gem_framebuffer_helper.h>
0024 #include <drm/drm_modeset_helper_vtables.h>
0025 #include <drm/drm_module.h>
0026 #include <drm/drm_probe_helper.h>
0027 #include <drm/drm_vblank.h>
0028 
0029 #include "meson_crtc.h"
0030 #include "meson_drv.h"
0031 #include "meson_overlay.h"
0032 #include "meson_plane.h"
0033 #include "meson_osd_afbcd.h"
0034 #include "meson_registers.h"
0035 #include "meson_encoder_cvbs.h"
0036 #include "meson_encoder_hdmi.h"
0037 #include "meson_viu.h"
0038 #include "meson_vpp.h"
0039 #include "meson_rdma.h"
0040 
0041 #define DRIVER_NAME "meson"
0042 #define DRIVER_DESC "Amlogic Meson DRM driver"
0043 
0044 /**
0045  * DOC: Video Processing Unit
0046  *
0047  * VPU Handles the Global Video Processing, it includes management of the
0048  * clocks gates, blocks reset lines and power domains.
0049  *
0050  * What is missing :
0051  *
0052  * - Full reset of entire video processing HW blocks
0053  * - Scaling and setup of the VPU clock
0054  * - Bus clock gates
0055  * - Powering up video processing HW blocks
0056  * - Powering Up HDMI controller and PHY
0057  */
0058 
0059 static const struct drm_mode_config_funcs meson_mode_config_funcs = {
0060     .atomic_check        = drm_atomic_helper_check,
0061     .atomic_commit       = drm_atomic_helper_commit,
0062     .fb_create           = drm_gem_fb_create,
0063 };
0064 
0065 static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = {
0066     .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
0067 };
0068 
0069 static irqreturn_t meson_irq(int irq, void *arg)
0070 {
0071     struct drm_device *dev = arg;
0072     struct meson_drm *priv = dev->dev_private;
0073 
0074     (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
0075 
0076     meson_crtc_irq(priv);
0077 
0078     return IRQ_HANDLED;
0079 }
0080 
0081 static int meson_dumb_create(struct drm_file *file, struct drm_device *dev,
0082                  struct drm_mode_create_dumb *args)
0083 {
0084     /*
0085      * We need 64bytes aligned stride, and PAGE aligned size
0086      */
0087     args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64);
0088     args->size = PAGE_ALIGN(args->pitch * args->height);
0089 
0090     return drm_gem_cma_dumb_create_internal(file, dev, args);
0091 }
0092 
0093 DEFINE_DRM_GEM_CMA_FOPS(fops);
0094 
0095 static const struct drm_driver meson_driver = {
0096     .driver_features    = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
0097 
0098     /* CMA Ops */
0099     DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
0100 
0101     /* Misc */
0102     .fops           = &fops,
0103     .name           = DRIVER_NAME,
0104     .desc           = DRIVER_DESC,
0105     .date           = "20161109",
0106     .major          = 1,
0107     .minor          = 0,
0108 };
0109 
0110 static bool meson_vpu_has_available_connectors(struct device *dev)
0111 {
0112     struct device_node *ep, *remote;
0113 
0114     /* Parses each endpoint and check if remote exists */
0115     for_each_endpoint_of_node(dev->of_node, ep) {
0116         /* If the endpoint node exists, consider it enabled */
0117         remote = of_graph_get_remote_port(ep);
0118         if (remote) {
0119             of_node_put(remote);
0120             of_node_put(ep);
0121             return true;
0122         }
0123     }
0124 
0125     return false;
0126 }
0127 
0128 static struct regmap_config meson_regmap_config = {
0129     .reg_bits       = 32,
0130     .val_bits       = 32,
0131     .reg_stride     = 4,
0132     .max_register   = 0x1000,
0133 };
0134 
0135 static void meson_vpu_init(struct meson_drm *priv)
0136 {
0137     u32 value;
0138 
0139     /*
0140      * Slave dc0 and dc5 connected to master port 1.
0141      * By default other slaves are connected to master port 0.
0142      */
0143     value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
0144         VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
0145     writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
0146 
0147     /* Slave dc0 connected to master port 1 */
0148     value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
0149     writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
0150 
0151     /* Slave dc4 and dc7 connected to master port 1 */
0152     value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
0153         VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
0154     writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
0155 
0156     /* Slave dc1 connected to master port 1 */
0157     value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
0158     writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
0159 }
0160 
0161 struct meson_drm_soc_attr {
0162     struct meson_drm_soc_limits limits;
0163     const struct soc_device_attribute *attrs;
0164 };
0165 
0166 static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
0167     /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
0168     {
0169         .limits = {
0170             .max_hdmi_phy_freq = 1650000,
0171         },
0172         .attrs = (const struct soc_device_attribute []) {
0173             { .soc_id = "GXL (S805*)", },
0174             { /* sentinel */ }
0175         }
0176     },
0177 };
0178 
0179 static int meson_drv_bind_master(struct device *dev, bool has_components)
0180 {
0181     struct platform_device *pdev = to_platform_device(dev);
0182     const struct meson_drm_match_data *match;
0183     struct meson_drm *priv;
0184     struct drm_device *drm;
0185     struct resource *res;
0186     void __iomem *regs;
0187     int ret, i;
0188 
0189     /* Checks if an output connector is available */
0190     if (!meson_vpu_has_available_connectors(dev)) {
0191         dev_err(dev, "No output connector available\n");
0192         return -ENODEV;
0193     }
0194 
0195     match = of_device_get_match_data(dev);
0196     if (!match)
0197         return -ENODEV;
0198 
0199     drm = drm_dev_alloc(&meson_driver, dev);
0200     if (IS_ERR(drm))
0201         return PTR_ERR(drm);
0202 
0203     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0204     if (!priv) {
0205         ret = -ENOMEM;
0206         goto free_drm;
0207     }
0208     drm->dev_private = priv;
0209     priv->drm = drm;
0210     priv->dev = dev;
0211     priv->compat = match->compat;
0212     priv->afbcd.ops = match->afbcd_ops;
0213 
0214     regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
0215     if (IS_ERR(regs)) {
0216         ret = PTR_ERR(regs);
0217         goto free_drm;
0218     }
0219 
0220     priv->io_base = regs;
0221 
0222     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
0223     if (!res) {
0224         ret = -EINVAL;
0225         goto free_drm;
0226     }
0227     /* Simply ioremap since it may be a shared register zone */
0228     regs = devm_ioremap(dev, res->start, resource_size(res));
0229     if (!regs) {
0230         ret = -EADDRNOTAVAIL;
0231         goto free_drm;
0232     }
0233 
0234     priv->hhi = devm_regmap_init_mmio(dev, regs,
0235                       &meson_regmap_config);
0236     if (IS_ERR(priv->hhi)) {
0237         dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
0238         ret = PTR_ERR(priv->hhi);
0239         goto free_drm;
0240     }
0241 
0242     priv->canvas = meson_canvas_get(dev);
0243     if (IS_ERR(priv->canvas)) {
0244         ret = PTR_ERR(priv->canvas);
0245         goto free_drm;
0246     }
0247 
0248     ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
0249     if (ret)
0250         goto free_drm;
0251     ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
0252     if (ret) {
0253         meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
0254         goto free_drm;
0255     }
0256     ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
0257     if (ret) {
0258         meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
0259         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
0260         goto free_drm;
0261     }
0262     ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
0263     if (ret) {
0264         meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
0265         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
0266         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
0267         goto free_drm;
0268     }
0269 
0270     priv->vsync_irq = platform_get_irq(pdev, 0);
0271 
0272     ret = drm_vblank_init(drm, 1);
0273     if (ret)
0274         goto free_drm;
0275 
0276     /* Assign limits per soc revision/package */
0277     for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) {
0278         if (soc_device_match(meson_drm_soc_attrs[i].attrs)) {
0279             priv->limits = &meson_drm_soc_attrs[i].limits;
0280             break;
0281         }
0282     }
0283 
0284     /*
0285      * Remove early framebuffers (ie. simplefb). The framebuffer can be
0286      * located anywhere in RAM
0287      */
0288     ret = drm_aperture_remove_framebuffers(false, &meson_driver);
0289     if (ret)
0290         goto free_drm;
0291 
0292     ret = drmm_mode_config_init(drm);
0293     if (ret)
0294         goto free_drm;
0295     drm->mode_config.max_width = 3840;
0296     drm->mode_config.max_height = 2160;
0297     drm->mode_config.funcs = &meson_mode_config_funcs;
0298     drm->mode_config.helper_private = &meson_mode_config_helpers;
0299 
0300     /* Hardware Initialization */
0301 
0302     meson_vpu_init(priv);
0303     meson_venc_init(priv);
0304     meson_vpp_init(priv);
0305     meson_viu_init(priv);
0306     if (priv->afbcd.ops) {
0307         ret = priv->afbcd.ops->init(priv);
0308         if (ret)
0309             goto free_drm;
0310     }
0311 
0312     /* Encoder Initialization */
0313 
0314     ret = meson_encoder_cvbs_init(priv);
0315     if (ret)
0316         goto exit_afbcd;
0317 
0318     if (has_components) {
0319         ret = component_bind_all(drm->dev, drm);
0320         if (ret) {
0321             dev_err(drm->dev, "Couldn't bind all components\n");
0322             goto exit_afbcd;
0323         }
0324     }
0325 
0326     ret = meson_encoder_hdmi_init(priv);
0327     if (ret)
0328         goto exit_afbcd;
0329 
0330     ret = meson_plane_create(priv);
0331     if (ret)
0332         goto exit_afbcd;
0333 
0334     ret = meson_overlay_create(priv);
0335     if (ret)
0336         goto exit_afbcd;
0337 
0338     ret = meson_crtc_create(priv);
0339     if (ret)
0340         goto exit_afbcd;
0341 
0342     ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm);
0343     if (ret)
0344         goto exit_afbcd;
0345 
0346     drm_mode_config_reset(drm);
0347 
0348     drm_kms_helper_poll_init(drm);
0349 
0350     platform_set_drvdata(pdev, priv);
0351 
0352     ret = drm_dev_register(drm, 0);
0353     if (ret)
0354         goto uninstall_irq;
0355 
0356     drm_fbdev_generic_setup(drm, 32);
0357 
0358     return 0;
0359 
0360 uninstall_irq:
0361     free_irq(priv->vsync_irq, drm);
0362 exit_afbcd:
0363     if (priv->afbcd.ops)
0364         priv->afbcd.ops->exit(priv);
0365 free_drm:
0366     drm_dev_put(drm);
0367 
0368     return ret;
0369 }
0370 
0371 static int meson_drv_bind(struct device *dev)
0372 {
0373     return meson_drv_bind_master(dev, true);
0374 }
0375 
0376 static void meson_drv_unbind(struct device *dev)
0377 {
0378     struct meson_drm *priv = dev_get_drvdata(dev);
0379     struct drm_device *drm = priv->drm;
0380 
0381     if (priv->canvas) {
0382         meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
0383         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
0384         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
0385         meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
0386     }
0387 
0388     drm_dev_unregister(drm);
0389     drm_kms_helper_poll_fini(drm);
0390     drm_atomic_helper_shutdown(drm);
0391     component_unbind_all(dev, drm);
0392     free_irq(priv->vsync_irq, drm);
0393     drm_dev_put(drm);
0394 
0395     if (priv->afbcd.ops)
0396         priv->afbcd.ops->exit(priv);
0397 }
0398 
0399 static const struct component_master_ops meson_drv_master_ops = {
0400     .bind   = meson_drv_bind,
0401     .unbind = meson_drv_unbind,
0402 };
0403 
0404 static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
0405 {
0406     struct meson_drm *priv = dev_get_drvdata(dev);
0407 
0408     if (!priv)
0409         return 0;
0410 
0411     return drm_mode_config_helper_suspend(priv->drm);
0412 }
0413 
0414 static int __maybe_unused meson_drv_pm_resume(struct device *dev)
0415 {
0416     struct meson_drm *priv = dev_get_drvdata(dev);
0417 
0418     if (!priv)
0419         return 0;
0420 
0421     meson_vpu_init(priv);
0422     meson_venc_init(priv);
0423     meson_vpp_init(priv);
0424     meson_viu_init(priv);
0425     if (priv->afbcd.ops)
0426         priv->afbcd.ops->init(priv);
0427 
0428     return drm_mode_config_helper_resume(priv->drm);
0429 }
0430 
0431 static void meson_drv_shutdown(struct platform_device *pdev)
0432 {
0433     struct meson_drm *priv = dev_get_drvdata(&pdev->dev);
0434 
0435     if (!priv)
0436         return;
0437 
0438     drm_kms_helper_poll_fini(priv->drm);
0439     drm_atomic_helper_shutdown(priv->drm);
0440 }
0441 
0442 /* Possible connectors nodes to ignore */
0443 static const struct of_device_id connectors_match[] = {
0444     { .compatible = "composite-video-connector" },
0445     { .compatible = "svideo-connector" },
0446     {}
0447 };
0448 
0449 static int meson_drv_probe(struct platform_device *pdev)
0450 {
0451     struct component_match *match = NULL;
0452     struct device_node *np = pdev->dev.of_node;
0453     struct device_node *ep, *remote;
0454     int count = 0;
0455 
0456     for_each_endpoint_of_node(np, ep) {
0457         remote = of_graph_get_remote_port_parent(ep);
0458         if (!remote || !of_device_is_available(remote)) {
0459             of_node_put(remote);
0460             continue;
0461         }
0462 
0463         /* If an analog connector is detected, count it as an output */
0464         if (of_match_node(connectors_match, remote)) {
0465             ++count;
0466             of_node_put(remote);
0467             continue;
0468         }
0469 
0470         dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n",
0471             np, remote, dev_name(&pdev->dev));
0472 
0473         component_match_add(&pdev->dev, &match, component_compare_of, remote);
0474 
0475         of_node_put(remote);
0476 
0477         ++count;
0478     }
0479 
0480     if (count && !match)
0481         return meson_drv_bind_master(&pdev->dev, false);
0482 
0483     /* If some endpoints were found, initialize the nodes */
0484     if (count) {
0485         dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count);
0486 
0487         return component_master_add_with_match(&pdev->dev,
0488                                &meson_drv_master_ops,
0489                                match);
0490     }
0491 
0492     /* If no output endpoints were available, simply bail out */
0493     return 0;
0494 };
0495 
0496 static struct meson_drm_match_data meson_drm_gxbb_data = {
0497     .compat = VPU_COMPATIBLE_GXBB,
0498 };
0499 
0500 static struct meson_drm_match_data meson_drm_gxl_data = {
0501     .compat = VPU_COMPATIBLE_GXL,
0502 };
0503 
0504 static struct meson_drm_match_data meson_drm_gxm_data = {
0505     .compat = VPU_COMPATIBLE_GXM,
0506     .afbcd_ops = &meson_afbcd_gxm_ops,
0507 };
0508 
0509 static struct meson_drm_match_data meson_drm_g12a_data = {
0510     .compat = VPU_COMPATIBLE_G12A,
0511     .afbcd_ops = &meson_afbcd_g12a_ops,
0512 };
0513 
0514 static const struct of_device_id dt_match[] = {
0515     { .compatible = "amlogic,meson-gxbb-vpu",
0516       .data       = (void *)&meson_drm_gxbb_data },
0517     { .compatible = "amlogic,meson-gxl-vpu",
0518       .data       = (void *)&meson_drm_gxl_data },
0519     { .compatible = "amlogic,meson-gxm-vpu",
0520       .data       = (void *)&meson_drm_gxm_data },
0521     { .compatible = "amlogic,meson-g12a-vpu",
0522       .data       = (void *)&meson_drm_g12a_data },
0523     {}
0524 };
0525 MODULE_DEVICE_TABLE(of, dt_match);
0526 
0527 static const struct dev_pm_ops meson_drv_pm_ops = {
0528     SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume)
0529 };
0530 
0531 static struct platform_driver meson_drm_platform_driver = {
0532     .probe      = meson_drv_probe,
0533     .shutdown   = meson_drv_shutdown,
0534     .driver     = {
0535         .name   = "meson-drm",
0536         .of_match_table = dt_match,
0537         .pm = &meson_drv_pm_ops,
0538     },
0539 };
0540 
0541 drm_module_platform_driver(meson_drm_platform_driver);
0542 
0543 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
0544 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0545 MODULE_DESCRIPTION(DRIVER_DESC);
0546 MODULE_LICENSE("GPL");