Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * ZynqMP DisplayPort Subsystem Driver
0004  *
0005  * Copyright (C) 2017 - 2020 Xilinx, Inc.
0006  *
0007  * Authors:
0008  * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
0009  * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
0010  */
0011 
0012 #include <linux/clk.h>
0013 #include <linux/dma-mapping.h>
0014 #include <linux/module.h>
0015 #include <linux/of_reserved_mem.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/pm_runtime.h>
0018 
0019 #include <drm/drm_atomic_helper.h>
0020 #include <drm/drm_device.h>
0021 #include <drm/drm_drv.h>
0022 #include <drm/drm_fb_helper.h>
0023 #include <drm/drm_fourcc.h>
0024 #include <drm/drm_gem_cma_helper.h>
0025 #include <drm/drm_gem_framebuffer_helper.h>
0026 #include <drm/drm_managed.h>
0027 #include <drm/drm_mode_config.h>
0028 #include <drm/drm_module.h>
0029 #include <drm/drm_probe_helper.h>
0030 #include <drm/drm_vblank.h>
0031 
0032 #include "zynqmp_disp.h"
0033 #include "zynqmp_dp.h"
0034 #include "zynqmp_dpsub.h"
0035 
0036 /* -----------------------------------------------------------------------------
0037  * Dumb Buffer & Framebuffer Allocation
0038  */
0039 
0040 static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv,
0041                     struct drm_device *drm,
0042                     struct drm_mode_create_dumb *args)
0043 {
0044     struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
0045     unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
0046 
0047     /* Enforce the alignment constraints of the DMA engine. */
0048     args->pitch = ALIGN(pitch, dpsub->dma_align);
0049 
0050     return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
0051 }
0052 
0053 static struct drm_framebuffer *
0054 zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv,
0055                const struct drm_mode_fb_cmd2 *mode_cmd)
0056 {
0057     struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
0058     struct drm_mode_fb_cmd2 cmd = *mode_cmd;
0059     unsigned int i;
0060 
0061     /* Enforce the alignment constraints of the DMA engine. */
0062     for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i)
0063         cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align);
0064 
0065     return drm_gem_fb_create(drm, file_priv, &cmd);
0066 }
0067 
0068 static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = {
0069     .fb_create      = zynqmp_dpsub_fb_create,
0070     .atomic_check       = drm_atomic_helper_check,
0071     .atomic_commit      = drm_atomic_helper_commit,
0072 };
0073 
0074 /* -----------------------------------------------------------------------------
0075  * DRM/KMS Driver
0076  */
0077 
0078 DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops);
0079 
0080 static const struct drm_driver zynqmp_dpsub_drm_driver = {
0081     .driver_features        = DRIVER_MODESET | DRIVER_GEM |
0082                       DRIVER_ATOMIC,
0083 
0084     DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(zynqmp_dpsub_dumb_create),
0085 
0086     .fops               = &zynqmp_dpsub_drm_fops,
0087 
0088     .name               = "zynqmp-dpsub",
0089     .desc               = "Xilinx DisplayPort Subsystem Driver",
0090     .date               = "20130509",
0091     .major              = 1,
0092     .minor              = 0,
0093 };
0094 
0095 static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
0096 {
0097     struct drm_device *drm = &dpsub->drm;
0098     int ret;
0099 
0100     /* Initialize mode config, vblank and the KMS poll helper. */
0101     ret = drmm_mode_config_init(drm);
0102     if (ret < 0)
0103         return ret;
0104 
0105     drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs;
0106     drm->mode_config.min_width = 0;
0107     drm->mode_config.min_height = 0;
0108     drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH;
0109     drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT;
0110 
0111     ret = drm_vblank_init(drm, 1);
0112     if (ret)
0113         return ret;
0114 
0115     drm_kms_helper_poll_init(drm);
0116 
0117     /*
0118      * Initialize the DISP and DP components. This will creates planes,
0119      * CRTC, encoder and connector. The DISP should be initialized first as
0120      * the DP encoder needs the CRTC.
0121      */
0122     ret = zynqmp_disp_drm_init(dpsub);
0123     if (ret)
0124         goto err_poll_fini;
0125 
0126     ret = zynqmp_dp_drm_init(dpsub);
0127     if (ret)
0128         goto err_poll_fini;
0129 
0130     /* Reset all components and register the DRM device. */
0131     drm_mode_config_reset(drm);
0132 
0133     ret = drm_dev_register(drm, 0);
0134     if (ret < 0)
0135         goto err_poll_fini;
0136 
0137     /* Initialize fbdev generic emulation. */
0138     drm_fbdev_generic_setup(drm, 24);
0139 
0140     return 0;
0141 
0142 err_poll_fini:
0143     drm_kms_helper_poll_fini(drm);
0144     return ret;
0145 }
0146 
0147 /* -----------------------------------------------------------------------------
0148  * Power Management
0149  */
0150 
0151 static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev)
0152 {
0153     struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
0154 
0155     return drm_mode_config_helper_suspend(&dpsub->drm);
0156 }
0157 
0158 static int __maybe_unused zynqmp_dpsub_resume(struct device *dev)
0159 {
0160     struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
0161 
0162     return drm_mode_config_helper_resume(&dpsub->drm);
0163 }
0164 
0165 static const struct dev_pm_ops zynqmp_dpsub_pm_ops = {
0166     SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume)
0167 };
0168 
0169 /* -----------------------------------------------------------------------------
0170  * Probe & Remove
0171  */
0172 
0173 static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
0174 {
0175     int ret;
0176 
0177     dpsub->apb_clk = devm_clk_get(dpsub->dev, "dp_apb_clk");
0178     if (IS_ERR(dpsub->apb_clk))
0179         return PTR_ERR(dpsub->apb_clk);
0180 
0181     ret = clk_prepare_enable(dpsub->apb_clk);
0182     if (ret) {
0183         dev_err(dpsub->dev, "failed to enable the APB clock\n");
0184         return ret;
0185     }
0186 
0187     return 0;
0188 }
0189 
0190 static int zynqmp_dpsub_probe(struct platform_device *pdev)
0191 {
0192     struct zynqmp_dpsub *dpsub;
0193     int ret;
0194 
0195     /* Allocate private data. */
0196     dpsub = devm_drm_dev_alloc(&pdev->dev, &zynqmp_dpsub_drm_driver,
0197                    struct zynqmp_dpsub, drm);
0198     if (IS_ERR(dpsub))
0199         return PTR_ERR(dpsub);
0200 
0201     dpsub->dev = &pdev->dev;
0202     platform_set_drvdata(pdev, dpsub);
0203 
0204     dma_set_mask(dpsub->dev, DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT));
0205 
0206     /* Try the reserved memory. Proceed if there's none. */
0207     of_reserved_mem_device_init(&pdev->dev);
0208 
0209     ret = zynqmp_dpsub_init_clocks(dpsub);
0210     if (ret < 0)
0211         goto err_mem;
0212 
0213     pm_runtime_enable(&pdev->dev);
0214 
0215     /*
0216      * DP should be probed first so that the zynqmp_disp can set the output
0217      * format accordingly.
0218      */
0219     ret = zynqmp_dp_probe(dpsub, &dpsub->drm);
0220     if (ret)
0221         goto err_pm;
0222 
0223     ret = zynqmp_disp_probe(dpsub, &dpsub->drm);
0224     if (ret)
0225         goto err_dp;
0226 
0227     ret = zynqmp_dpsub_drm_init(dpsub);
0228     if (ret)
0229         goto err_disp;
0230 
0231     dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed");
0232 
0233     return 0;
0234 
0235 err_disp:
0236     zynqmp_disp_remove(dpsub);
0237 err_dp:
0238     zynqmp_dp_remove(dpsub);
0239 err_pm:
0240     pm_runtime_disable(&pdev->dev);
0241     clk_disable_unprepare(dpsub->apb_clk);
0242 err_mem:
0243     of_reserved_mem_device_release(&pdev->dev);
0244     return ret;
0245 }
0246 
0247 static int zynqmp_dpsub_remove(struct platform_device *pdev)
0248 {
0249     struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
0250     struct drm_device *drm = &dpsub->drm;
0251 
0252     drm_dev_unregister(drm);
0253     drm_atomic_helper_shutdown(drm);
0254     drm_kms_helper_poll_fini(drm);
0255 
0256     zynqmp_disp_remove(dpsub);
0257     zynqmp_dp_remove(dpsub);
0258 
0259     pm_runtime_disable(&pdev->dev);
0260     clk_disable_unprepare(dpsub->apb_clk);
0261     of_reserved_mem_device_release(&pdev->dev);
0262 
0263     return 0;
0264 }
0265 
0266 static void zynqmp_dpsub_shutdown(struct platform_device *pdev)
0267 {
0268     struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
0269 
0270     drm_atomic_helper_shutdown(&dpsub->drm);
0271 }
0272 
0273 static const struct of_device_id zynqmp_dpsub_of_match[] = {
0274     { .compatible = "xlnx,zynqmp-dpsub-1.7", },
0275     { /* end of table */ },
0276 };
0277 MODULE_DEVICE_TABLE(of, zynqmp_dpsub_of_match);
0278 
0279 static struct platform_driver zynqmp_dpsub_driver = {
0280     .probe          = zynqmp_dpsub_probe,
0281     .remove         = zynqmp_dpsub_remove,
0282     .shutdown       = zynqmp_dpsub_shutdown,
0283     .driver         = {
0284         .name       = "zynqmp-dpsub",
0285         .pm     = &zynqmp_dpsub_pm_ops,
0286         .of_match_table = zynqmp_dpsub_of_match,
0287     },
0288 };
0289 
0290 drm_module_platform_driver(zynqmp_dpsub_driver);
0291 
0292 MODULE_AUTHOR("Xilinx, Inc.");
0293 MODULE_DESCRIPTION("ZynqMP DP Subsystem Driver");
0294 MODULE_LICENSE("GPL v2");