Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP.
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/of_device.h>
0008 #include <linux/of_graph.h>
0009 #include <linux/pm_runtime.h>
0010 #include <linux/slab.h>
0011 #include <drm/drm_bridge_connector.h>
0012 #include <drm/drm_device.h>
0013 #include <drm/drm_modeset_helper.h>
0014 
0015 #include "dcss-dev.h"
0016 #include "dcss-kms.h"
0017 
0018 static void dcss_clocks_enable(struct dcss_dev *dcss)
0019 {
0020     clk_prepare_enable(dcss->axi_clk);
0021     clk_prepare_enable(dcss->apb_clk);
0022     clk_prepare_enable(dcss->rtrm_clk);
0023     clk_prepare_enable(dcss->dtrc_clk);
0024     clk_prepare_enable(dcss->pix_clk);
0025 }
0026 
0027 static void dcss_clocks_disable(struct dcss_dev *dcss)
0028 {
0029     clk_disable_unprepare(dcss->pix_clk);
0030     clk_disable_unprepare(dcss->dtrc_clk);
0031     clk_disable_unprepare(dcss->rtrm_clk);
0032     clk_disable_unprepare(dcss->apb_clk);
0033     clk_disable_unprepare(dcss->axi_clk);
0034 }
0035 
0036 static void dcss_disable_dtg_and_ss_cb(void *data)
0037 {
0038     struct dcss_dev *dcss = data;
0039 
0040     dcss->disable_callback = NULL;
0041 
0042     dcss_ss_shutoff(dcss->ss);
0043     dcss_dtg_shutoff(dcss->dtg);
0044 
0045     complete(&dcss->disable_completion);
0046 }
0047 
0048 void dcss_disable_dtg_and_ss(struct dcss_dev *dcss)
0049 {
0050     dcss->disable_callback = dcss_disable_dtg_and_ss_cb;
0051 }
0052 
0053 void dcss_enable_dtg_and_ss(struct dcss_dev *dcss)
0054 {
0055     if (dcss->disable_callback)
0056         dcss->disable_callback = NULL;
0057 
0058     dcss_dtg_enable(dcss->dtg);
0059     dcss_ss_enable(dcss->ss);
0060 }
0061 
0062 static int dcss_submodules_init(struct dcss_dev *dcss)
0063 {
0064     int ret = 0;
0065     u32 base_addr = dcss->start_addr;
0066     const struct dcss_type_data *devtype = dcss->devtype;
0067 
0068     dcss_clocks_enable(dcss);
0069 
0070     ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs);
0071     if (ret)
0072         return ret;
0073 
0074     ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs);
0075     if (ret)
0076         goto ctxld_err;
0077 
0078     ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs);
0079     if (ret)
0080         goto dtg_err;
0081 
0082     ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs);
0083     if (ret)
0084         goto ss_err;
0085 
0086     ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs);
0087     if (ret)
0088         goto dpr_err;
0089 
0090     ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs);
0091     if (ret)
0092         goto scaler_err;
0093 
0094     dcss_clocks_disable(dcss);
0095 
0096     return 0;
0097 
0098 scaler_err:
0099     dcss_dpr_exit(dcss->dpr);
0100 
0101 dpr_err:
0102     dcss_ss_exit(dcss->ss);
0103 
0104 ss_err:
0105     dcss_dtg_exit(dcss->dtg);
0106 
0107 dtg_err:
0108     dcss_ctxld_exit(dcss->ctxld);
0109 
0110 ctxld_err:
0111     dcss_blkctl_exit(dcss->blkctl);
0112 
0113     dcss_clocks_disable(dcss);
0114 
0115     return ret;
0116 }
0117 
0118 static void dcss_submodules_stop(struct dcss_dev *dcss)
0119 {
0120     dcss_clocks_enable(dcss);
0121     dcss_scaler_exit(dcss->scaler);
0122     dcss_dpr_exit(dcss->dpr);
0123     dcss_ss_exit(dcss->ss);
0124     dcss_dtg_exit(dcss->dtg);
0125     dcss_ctxld_exit(dcss->ctxld);
0126     dcss_blkctl_exit(dcss->blkctl);
0127     dcss_clocks_disable(dcss);
0128 }
0129 
0130 static int dcss_clks_init(struct dcss_dev *dcss)
0131 {
0132     int i;
0133     struct {
0134         const char *id;
0135         struct clk **clk;
0136     } clks[] = {
0137         {"apb",   &dcss->apb_clk},
0138         {"axi",   &dcss->axi_clk},
0139         {"pix",   &dcss->pix_clk},
0140         {"rtrm",  &dcss->rtrm_clk},
0141         {"dtrc",  &dcss->dtrc_clk},
0142     };
0143 
0144     for (i = 0; i < ARRAY_SIZE(clks); i++) {
0145         *clks[i].clk = devm_clk_get(dcss->dev, clks[i].id);
0146         if (IS_ERR(*clks[i].clk)) {
0147             dev_err(dcss->dev, "failed to get %s clock\n",
0148                 clks[i].id);
0149             return PTR_ERR(*clks[i].clk);
0150         }
0151     }
0152 
0153     return 0;
0154 }
0155 
0156 static void dcss_clks_release(struct dcss_dev *dcss)
0157 {
0158     devm_clk_put(dcss->dev, dcss->dtrc_clk);
0159     devm_clk_put(dcss->dev, dcss->rtrm_clk);
0160     devm_clk_put(dcss->dev, dcss->pix_clk);
0161     devm_clk_put(dcss->dev, dcss->axi_clk);
0162     devm_clk_put(dcss->dev, dcss->apb_clk);
0163 }
0164 
0165 struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
0166 {
0167     struct platform_device *pdev = to_platform_device(dev);
0168     int ret;
0169     struct resource *res;
0170     struct dcss_dev *dcss;
0171     const struct dcss_type_data *devtype;
0172 
0173     devtype = of_device_get_match_data(dev);
0174     if (!devtype) {
0175         dev_err(dev, "no device match found\n");
0176         return ERR_PTR(-ENODEV);
0177     }
0178 
0179     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0180     if (!res) {
0181         dev_err(dev, "cannot get memory resource\n");
0182         return ERR_PTR(-EINVAL);
0183     }
0184 
0185     dcss = kzalloc(sizeof(*dcss), GFP_KERNEL);
0186     if (!dcss)
0187         return ERR_PTR(-ENOMEM);
0188 
0189     dcss->dev = dev;
0190     dcss->devtype = devtype;
0191     dcss->hdmi_output = hdmi_output;
0192 
0193     ret = dcss_clks_init(dcss);
0194     if (ret) {
0195         dev_err(dev, "clocks initialization failed\n");
0196         goto err;
0197     }
0198 
0199     dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0);
0200     if (!dcss->of_port) {
0201         dev_err(dev, "no port@0 node in %s\n", dev->of_node->full_name);
0202         ret = -ENODEV;
0203         goto clks_err;
0204     }
0205 
0206     dcss->start_addr = res->start;
0207 
0208     ret = dcss_submodules_init(dcss);
0209     if (ret) {
0210         of_node_put(dcss->of_port);
0211         dev_err(dev, "submodules initialization failed\n");
0212         goto clks_err;
0213     }
0214 
0215     init_completion(&dcss->disable_completion);
0216 
0217     pm_runtime_set_autosuspend_delay(dev, 100);
0218     pm_runtime_use_autosuspend(dev);
0219     pm_runtime_set_suspended(dev);
0220     pm_runtime_allow(dev);
0221     pm_runtime_enable(dev);
0222 
0223     return dcss;
0224 
0225 clks_err:
0226     dcss_clks_release(dcss);
0227 
0228 err:
0229     kfree(dcss);
0230 
0231     return ERR_PTR(ret);
0232 }
0233 
0234 void dcss_dev_destroy(struct dcss_dev *dcss)
0235 {
0236     if (!pm_runtime_suspended(dcss->dev)) {
0237         dcss_ctxld_suspend(dcss->ctxld);
0238         dcss_clocks_disable(dcss);
0239     }
0240 
0241     of_node_put(dcss->of_port);
0242 
0243     pm_runtime_disable(dcss->dev);
0244 
0245     dcss_submodules_stop(dcss);
0246 
0247     dcss_clks_release(dcss);
0248 
0249     kfree(dcss);
0250 }
0251 
0252 #ifdef CONFIG_PM_SLEEP
0253 int dcss_dev_suspend(struct device *dev)
0254 {
0255     struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
0256     struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
0257     struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
0258     int ret;
0259 
0260     drm_bridge_connector_disable_hpd(kms->connector);
0261 
0262     drm_mode_config_helper_suspend(ddev);
0263 
0264     if (pm_runtime_suspended(dev))
0265         return 0;
0266 
0267     ret = dcss_ctxld_suspend(dcss->ctxld);
0268     if (ret)
0269         return ret;
0270 
0271     dcss_clocks_disable(dcss);
0272 
0273     return 0;
0274 }
0275 
0276 int dcss_dev_resume(struct device *dev)
0277 {
0278     struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
0279     struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
0280     struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
0281 
0282     if (pm_runtime_suspended(dev)) {
0283         drm_mode_config_helper_resume(ddev);
0284         return 0;
0285     }
0286 
0287     dcss_clocks_enable(dcss);
0288 
0289     dcss_blkctl_cfg(dcss->blkctl);
0290 
0291     dcss_ctxld_resume(dcss->ctxld);
0292 
0293     drm_mode_config_helper_resume(ddev);
0294 
0295     drm_bridge_connector_enable_hpd(kms->connector);
0296 
0297     return 0;
0298 }
0299 #endif /* CONFIG_PM_SLEEP */
0300 
0301 #ifdef CONFIG_PM
0302 int dcss_dev_runtime_suspend(struct device *dev)
0303 {
0304     struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
0305     int ret;
0306 
0307     ret = dcss_ctxld_suspend(dcss->ctxld);
0308     if (ret)
0309         return ret;
0310 
0311     dcss_clocks_disable(dcss);
0312 
0313     return 0;
0314 }
0315 
0316 int dcss_dev_runtime_resume(struct device *dev)
0317 {
0318     struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
0319 
0320     dcss_clocks_enable(dcss);
0321 
0322     dcss_blkctl_cfg(dcss->blkctl);
0323 
0324     dcss_ctxld_resume(dcss->ctxld);
0325 
0326     return 0;
0327 }
0328 #endif /* CONFIG_PM */