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