1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/component.h> 8 #include <linux/device.h> 9 #include <linux/dma-mapping.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_platform.h> 13 #include <linux/platform_device.h> 14 #include <linux/pm.h> 15 #include <linux/pm_runtime.h> 16 17 #include <drm/clients/drm_client_setup.h> 18 #include <drm/drm_atomic_helper.h> 19 #include <drm/drm_drv.h> 20 #include <drm/drm_fbdev_dma.h> 21 #include <drm/drm_fourcc.h> 22 #include <drm/drm_gem_dma_helper.h> 23 #include <drm/drm_managed.h> 24 #include <drm/drm_modeset_helper.h> 25 #include <drm/drm_of.h> 26 27 #include "dc-de.h" 28 #include "dc-drv.h" 29 #include "dc-pe.h" 30 31 struct dc_priv { 32 struct drm_device *drm; 33 struct clk *clk_cfg; 34 }; 35 36 DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops); 37 38 static struct drm_driver dc_drm_driver = { 39 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 40 DRM_GEM_DMA_DRIVER_OPS, 41 DRM_FBDEV_DMA_DRIVER_OPS, 42 .fops = &dc_drm_driver_fops, 43 .name = "imx8-dc", 44 .desc = "i.MX8 DC DRM graphics", 45 .major = 1, 46 .minor = 0, 47 .patchlevel = 0, 48 }; 49 50 static void 51 dc_add_components(struct device *dev, struct component_match **matchptr) 52 { 53 struct device_node *child, *grandchild; 54 55 for_each_available_child_of_node(dev->of_node, child) { 56 /* The interrupt controller is not a component. */ 57 if (of_device_is_compatible(child, "fsl,imx8qxp-dc-intc")) 58 continue; 59 60 drm_of_component_match_add(dev, matchptr, component_compare_of, 61 child); 62 63 for_each_available_child_of_node(child, grandchild) 64 drm_of_component_match_add(dev, matchptr, 65 component_compare_of, 66 grandchild); 67 } 68 } 69 70 static int dc_drm_component_bind_all(struct dc_drm_device *dc_drm) 71 { 72 struct drm_device *drm = &dc_drm->base; 73 int ret; 74 75 ret = component_bind_all(drm->dev, dc_drm); 76 if (ret) 77 return ret; 78 79 dc_de_post_bind(dc_drm); 80 dc_pe_post_bind(dc_drm); 81 82 return 0; 83 } 84 85 static void dc_drm_component_unbind_all(void *ptr) 86 { 87 struct dc_drm_device *dc_drm = ptr; 88 struct drm_device *drm = &dc_drm->base; 89 90 component_unbind_all(drm->dev, dc_drm); 91 } 92 93 static int dc_drm_bind(struct device *dev) 94 { 95 struct dc_priv *priv = dev_get_drvdata(dev); 96 struct dc_drm_device *dc_drm; 97 struct drm_device *drm; 98 int ret; 99 100 dc_drm = devm_drm_dev_alloc(dev, &dc_drm_driver, struct dc_drm_device, 101 base); 102 if (IS_ERR(dc_drm)) 103 return PTR_ERR(dc_drm); 104 105 drm = &dc_drm->base; 106 107 ret = dc_drm_component_bind_all(dc_drm); 108 if (ret) 109 return ret; 110 111 ret = devm_add_action_or_reset(dev, dc_drm_component_unbind_all, 112 dc_drm); 113 if (ret) 114 return ret; 115 116 ret = dc_kms_init(dc_drm); 117 if (ret) 118 return ret; 119 120 ret = drm_dev_register(drm, 0); 121 if (ret) { 122 dev_err(dev, "failed to register drm device: %d\n", ret); 123 goto err; 124 } 125 126 drm_client_setup_with_fourcc(drm, DRM_FORMAT_XRGB8888); 127 128 priv->drm = drm; 129 130 return 0; 131 132 err: 133 dc_kms_uninit(dc_drm); 134 135 return ret; 136 } 137 138 static void dc_drm_unbind(struct device *dev) 139 { 140 struct dc_priv *priv = dev_get_drvdata(dev); 141 struct dc_drm_device *dc_drm = to_dc_drm_device(priv->drm); 142 struct drm_device *drm = &dc_drm->base; 143 144 priv->drm = NULL; 145 drm_dev_unplug(drm); 146 dc_kms_uninit(dc_drm); 147 drm_atomic_helper_shutdown(drm); 148 } 149 150 static const struct component_master_ops dc_drm_ops = { 151 .bind = dc_drm_bind, 152 .unbind = dc_drm_unbind, 153 }; 154 155 static int dc_probe(struct platform_device *pdev) 156 { 157 struct component_match *match = NULL; 158 struct dc_priv *priv; 159 int ret; 160 161 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 162 if (!priv) 163 return -ENOMEM; 164 165 priv->clk_cfg = devm_clk_get(&pdev->dev, NULL); 166 if (IS_ERR(priv->clk_cfg)) 167 return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg), 168 "failed to get cfg clock\n"); 169 170 dev_set_drvdata(&pdev->dev, priv); 171 172 ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 173 if (ret) 174 return ret; 175 176 ret = devm_pm_runtime_enable(&pdev->dev); 177 if (ret) 178 return ret; 179 180 ret = devm_of_platform_populate(&pdev->dev); 181 if (ret) 182 return ret; 183 184 dc_add_components(&pdev->dev, &match); 185 186 ret = component_master_add_with_match(&pdev->dev, &dc_drm_ops, match); 187 if (ret) 188 return dev_err_probe(&pdev->dev, ret, 189 "failed to add component master\n"); 190 191 return 0; 192 } 193 194 static void dc_remove(struct platform_device *pdev) 195 { 196 component_master_del(&pdev->dev, &dc_drm_ops); 197 } 198 199 static int dc_runtime_suspend(struct device *dev) 200 { 201 struct dc_priv *priv = dev_get_drvdata(dev); 202 203 clk_disable_unprepare(priv->clk_cfg); 204 205 return 0; 206 } 207 208 static int dc_runtime_resume(struct device *dev) 209 { 210 struct dc_priv *priv = dev_get_drvdata(dev); 211 int ret; 212 213 ret = clk_prepare_enable(priv->clk_cfg); 214 if (ret) 215 dev_err(dev, "failed to enable cfg clock: %d\n", ret); 216 217 return ret; 218 } 219 220 static int dc_suspend(struct device *dev) 221 { 222 struct dc_priv *priv = dev_get_drvdata(dev); 223 224 return drm_mode_config_helper_suspend(priv->drm); 225 } 226 227 static int dc_resume(struct device *dev) 228 { 229 struct dc_priv *priv = dev_get_drvdata(dev); 230 231 return drm_mode_config_helper_resume(priv->drm); 232 } 233 234 static void dc_shutdown(struct platform_device *pdev) 235 { 236 struct dc_priv *priv = dev_get_drvdata(&pdev->dev); 237 238 drm_atomic_helper_shutdown(priv->drm); 239 } 240 241 static const struct dev_pm_ops dc_pm_ops = { 242 RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL) 243 SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume) 244 }; 245 246 static const struct of_device_id dc_dt_ids[] = { 247 { .compatible = "fsl,imx8qxp-dc", }, 248 { /* sentinel */ } 249 }; 250 MODULE_DEVICE_TABLE(of, dc_dt_ids); 251 252 static struct platform_driver dc_driver = { 253 .probe = dc_probe, 254 .remove = dc_remove, 255 .shutdown = dc_shutdown, 256 .driver = { 257 .name = "imx8-dc", 258 .of_match_table = dc_dt_ids, 259 .pm = pm_sleep_ptr(&dc_pm_ops), 260 }, 261 }; 262 263 static struct platform_driver * const dc_drivers[] = { 264 &dc_cf_driver, 265 &dc_de_driver, 266 &dc_ed_driver, 267 &dc_fg_driver, 268 &dc_fl_driver, 269 &dc_fw_driver, 270 &dc_ic_driver, 271 &dc_lb_driver, 272 &dc_pe_driver, 273 &dc_tc_driver, 274 &dc_driver, 275 }; 276 277 static int __init dc_drm_init(void) 278 { 279 return platform_register_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); 280 } 281 282 static void __exit dc_drm_exit(void) 283 { 284 platform_unregister_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); 285 } 286 287 module_init(dc_drm_init); 288 module_exit(dc_drm_exit); 289 290 MODULE_DESCRIPTION("i.MX8 Display Controller DRM Driver"); 291 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); 292 MODULE_LICENSE("GPL"); 293