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