1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Hisilicon Kirin SoCs drm master driver 4 * 5 * Copyright (c) 2016 Linaro Limited. 6 * Copyright (c) 2014-2016 HiSilicon Limited. 7 * 8 * Author: 9 * Xinliang Liu <z.liuxinliang@hisilicon.com> 10 * Xinliang Liu <xinliang.liu@linaro.org> 11 * Xinwei Kong <kong.kongxinwei@hisilicon.com> 12 */ 13 14 #include <linux/of_platform.h> 15 #include <linux/component.h> 16 #include <linux/module.h> 17 #include <linux/of_graph.h> 18 #include <linux/platform_device.h> 19 20 #include <drm/drm_atomic_helper.h> 21 #include <drm/drm_drv.h> 22 #include <drm/drm_fb_helper.h> 23 #include <drm/drm_gem_dma_helper.h> 24 #include <drm/drm_gem_framebuffer_helper.h> 25 #include <drm/drm_module.h> 26 #include <drm/drm_of.h> 27 #include <drm/drm_probe_helper.h> 28 #include <drm/drm_vblank.h> 29 30 #include "kirin_drm_drv.h" 31 32 #define KIRIN_MAX_PLANE 2 33 34 struct kirin_drm_private { 35 struct kirin_crtc crtc; 36 struct kirin_plane planes[KIRIN_MAX_PLANE]; 37 void *hw_ctx; 38 }; 39 40 static int kirin_drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 41 struct drm_plane *plane, 42 const struct kirin_drm_data *driver_data) 43 { 44 struct device_node *port; 45 int ret; 46 47 /* set crtc port so that 48 * drm_of_find_possible_crtcs call works 49 */ 50 port = of_get_child_by_name(dev->dev->of_node, "port"); 51 if (!port) { 52 DRM_ERROR("no port node found in %pOF\n", dev->dev->of_node); 53 return -EINVAL; 54 } 55 of_node_put(port); 56 crtc->port = port; 57 58 ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, 59 driver_data->crtc_funcs, NULL); 60 if (ret) { 61 DRM_ERROR("failed to init crtc.\n"); 62 return ret; 63 } 64 65 drm_crtc_helper_add(crtc, driver_data->crtc_helper_funcs); 66 67 return 0; 68 } 69 70 static int kirin_drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 71 enum drm_plane_type type, 72 const struct kirin_drm_data *data) 73 { 74 int ret = 0; 75 76 ret = drm_universal_plane_init(dev, plane, 1, data->plane_funcs, 77 data->channel_formats, 78 data->channel_formats_cnt, 79 NULL, type, NULL); 80 if (ret) { 81 DRM_ERROR("fail to init plane, ch=%d\n", 0); 82 return ret; 83 } 84 85 drm_plane_helper_add(plane, data->plane_helper_funcs); 86 87 return 0; 88 } 89 90 static void kirin_drm_private_cleanup(struct drm_device *dev) 91 { 92 struct kirin_drm_private *kirin_priv = dev->dev_private; 93 struct kirin_drm_data *data; 94 95 data = (struct kirin_drm_data *)of_device_get_match_data(dev->dev); 96 if (data->cleanup_hw_ctx) 97 data->cleanup_hw_ctx(kirin_priv->hw_ctx); 98 99 devm_kfree(dev->dev, kirin_priv); 100 dev->dev_private = NULL; 101 } 102 103 static int kirin_drm_private_init(struct drm_device *dev, 104 const struct kirin_drm_data *driver_data) 105 { 106 struct platform_device *pdev = to_platform_device(dev->dev); 107 struct kirin_drm_private *kirin_priv; 108 struct drm_plane *prim_plane; 109 enum drm_plane_type type; 110 void *ctx; 111 int ret; 112 u32 ch; 113 114 kirin_priv = devm_kzalloc(dev->dev, sizeof(*kirin_priv), GFP_KERNEL); 115 if (!kirin_priv) { 116 DRM_ERROR("failed to alloc kirin_drm_private\n"); 117 return -ENOMEM; 118 } 119 120 ctx = driver_data->alloc_hw_ctx(pdev, &kirin_priv->crtc.base); 121 if (IS_ERR(ctx)) { 122 DRM_ERROR("failed to initialize kirin_priv hw ctx\n"); 123 return -EINVAL; 124 } 125 kirin_priv->hw_ctx = ctx; 126 127 /* 128 * plane init 129 * TODO: Now only support primary plane, overlay planes 130 * need to do. 131 */ 132 for (ch = 0; ch < driver_data->num_planes; ch++) { 133 if (ch == driver_data->prim_plane) 134 type = DRM_PLANE_TYPE_PRIMARY; 135 else 136 type = DRM_PLANE_TYPE_OVERLAY; 137 ret = kirin_drm_plane_init(dev, &kirin_priv->planes[ch].base, 138 type, driver_data); 139 if (ret) 140 return ret; 141 kirin_priv->planes[ch].ch = ch; 142 kirin_priv->planes[ch].hw_ctx = ctx; 143 } 144 145 /* crtc init */ 146 prim_plane = &kirin_priv->planes[driver_data->prim_plane].base; 147 ret = kirin_drm_crtc_init(dev, &kirin_priv->crtc.base, 148 prim_plane, driver_data); 149 if (ret) 150 return ret; 151 kirin_priv->crtc.hw_ctx = ctx; 152 dev->dev_private = kirin_priv; 153 154 return 0; 155 } 156 157 static int kirin_drm_kms_init(struct drm_device *dev, 158 const struct kirin_drm_data *driver_data) 159 { 160 int ret; 161 162 /* dev->mode_config initialization */ 163 drm_mode_config_init(dev); 164 dev->mode_config.min_width = 0; 165 dev->mode_config.min_height = 0; 166 dev->mode_config.max_width = driver_data->config_max_width; 167 dev->mode_config.max_height = driver_data->config_max_width; 168 dev->mode_config.funcs = driver_data->mode_config_funcs; 169 170 /* display controller init */ 171 ret = kirin_drm_private_init(dev, driver_data); 172 if (ret) 173 goto err_mode_config_cleanup; 174 175 /* bind and init sub drivers */ 176 ret = component_bind_all(dev->dev, dev); 177 if (ret) { 178 DRM_ERROR("failed to bind all component.\n"); 179 goto err_private_cleanup; 180 } 181 182 /* vblank init */ 183 ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 184 if (ret) { 185 DRM_ERROR("failed to initialize vblank.\n"); 186 goto err_unbind_all; 187 } 188 189 /* reset all the states of crtc/plane/encoder/connector */ 190 drm_mode_config_reset(dev); 191 192 /* init kms poll for handling hpd */ 193 drm_kms_helper_poll_init(dev); 194 195 return 0; 196 197 err_unbind_all: 198 component_unbind_all(dev->dev, dev); 199 err_private_cleanup: 200 kirin_drm_private_cleanup(dev); 201 err_mode_config_cleanup: 202 drm_mode_config_cleanup(dev); 203 return ret; 204 } 205 206 static int kirin_drm_kms_cleanup(struct drm_device *dev) 207 { 208 drm_kms_helper_poll_fini(dev); 209 kirin_drm_private_cleanup(dev); 210 drm_mode_config_cleanup(dev); 211 212 return 0; 213 } 214 215 static int kirin_drm_bind(struct device *dev) 216 { 217 struct kirin_drm_data *driver_data; 218 struct drm_device *drm_dev; 219 int ret; 220 221 driver_data = (struct kirin_drm_data *)of_device_get_match_data(dev); 222 if (!driver_data) 223 return -EINVAL; 224 225 drm_dev = drm_dev_alloc(driver_data->driver, dev); 226 if (IS_ERR(drm_dev)) 227 return PTR_ERR(drm_dev); 228 dev_set_drvdata(dev, drm_dev); 229 230 /* display controller init */ 231 ret = kirin_drm_kms_init(drm_dev, driver_data); 232 if (ret) 233 goto err_drm_dev_put; 234 235 ret = drm_dev_register(drm_dev, 0); 236 if (ret) 237 goto err_kms_cleanup; 238 239 drm_fbdev_generic_setup(drm_dev, 32); 240 241 return 0; 242 243 err_kms_cleanup: 244 kirin_drm_kms_cleanup(drm_dev); 245 err_drm_dev_put: 246 drm_dev_put(drm_dev); 247 248 return ret; 249 } 250 251 static void kirin_drm_unbind(struct device *dev) 252 { 253 struct drm_device *drm_dev = dev_get_drvdata(dev); 254 255 drm_dev_unregister(drm_dev); 256 kirin_drm_kms_cleanup(drm_dev); 257 drm_dev_put(drm_dev); 258 } 259 260 static const struct component_master_ops kirin_drm_ops = { 261 .bind = kirin_drm_bind, 262 .unbind = kirin_drm_unbind, 263 }; 264 265 static int kirin_drm_platform_probe(struct platform_device *pdev) 266 { 267 struct device *dev = &pdev->dev; 268 struct device_node *np = dev->of_node; 269 struct component_match *match = NULL; 270 struct device_node *remote; 271 272 remote = of_graph_get_remote_node(np, 0, 0); 273 if (!remote) 274 return -ENODEV; 275 276 drm_of_component_match_add(dev, &match, component_compare_of, remote); 277 of_node_put(remote); 278 279 return component_master_add_with_match(dev, &kirin_drm_ops, match); 280 } 281 282 static int kirin_drm_platform_remove(struct platform_device *pdev) 283 { 284 component_master_del(&pdev->dev, &kirin_drm_ops); 285 return 0; 286 } 287 288 static const struct of_device_id kirin_drm_dt_ids[] = { 289 { .compatible = "hisilicon,hi6220-ade", 290 .data = &ade_driver_data, 291 }, 292 { /* end node */ }, 293 }; 294 MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); 295 296 static struct platform_driver kirin_drm_platform_driver = { 297 .probe = kirin_drm_platform_probe, 298 .remove = kirin_drm_platform_remove, 299 .driver = { 300 .name = "kirin-drm", 301 .of_match_table = kirin_drm_dt_ids, 302 }, 303 }; 304 305 drm_module_platform_driver(kirin_drm_platform_driver); 306 307 MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>"); 308 MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>"); 309 MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>"); 310 MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver"); 311 MODULE_LICENSE("GPL v2"); 312