320 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Hisilicon Kirin SoCs drm master driver4 *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/component.h>15#include <linux/module.h>16#include <linux/of.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_fbdev_dma.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 233 34struct kirin_drm_private {35 struct kirin_crtc crtc;36 struct kirin_plane planes[KIRIN_MAX_PLANE];37 void *hw_ctx;38};39 40static 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 that48 * drm_of_find_possible_crtcs call works49 */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 70static 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 90static 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 103static 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 init129 * TODO: Now only support primary plane, overlay planes130 * 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 else136 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 157static 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 197err_unbind_all:198 component_unbind_all(dev->dev, dev);199err_private_cleanup:200 kirin_drm_private_cleanup(dev);201err_mode_config_cleanup:202 drm_mode_config_cleanup(dev);203 return ret;204}205 206static int kirin_drm_kms_cleanup(struct drm_device *dev)207{208 drm_kms_helper_poll_fini(dev);209 drm_atomic_helper_shutdown(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_dma_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 dev_set_drvdata(dev, NULL);249 250 return ret;251}252 253static void kirin_drm_unbind(struct device *dev)254{255 struct drm_device *drm_dev = dev_get_drvdata(dev);256 257 drm_dev_unregister(drm_dev);258 kirin_drm_kms_cleanup(drm_dev);259 drm_dev_put(drm_dev);260 dev_set_drvdata(dev, NULL);261}262 263static const struct component_master_ops kirin_drm_ops = {264 .bind = kirin_drm_bind,265 .unbind = kirin_drm_unbind,266};267 268static int kirin_drm_platform_probe(struct platform_device *pdev)269{270 struct device *dev = &pdev->dev;271 struct device_node *np = dev->of_node;272 struct component_match *match = NULL;273 struct device_node *remote;274 275 remote = of_graph_get_remote_node(np, 0, 0);276 if (!remote)277 return -ENODEV;278 279 drm_of_component_match_add(dev, &match, component_compare_of, remote);280 of_node_put(remote);281 282 return component_master_add_with_match(dev, &kirin_drm_ops, match);283}284 285static void kirin_drm_platform_remove(struct platform_device *pdev)286{287 component_master_del(&pdev->dev, &kirin_drm_ops);288}289 290static void kirin_drm_platform_shutdown(struct platform_device *pdev)291{292 drm_atomic_helper_shutdown(platform_get_drvdata(pdev));293}294 295static const struct of_device_id kirin_drm_dt_ids[] = {296 { .compatible = "hisilicon,hi6220-ade",297 .data = &ade_driver_data,298 },299 { /* end node */ },300};301MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);302 303static struct platform_driver kirin_drm_platform_driver = {304 .probe = kirin_drm_platform_probe,305 .remove_new = kirin_drm_platform_remove,306 .shutdown = kirin_drm_platform_shutdown,307 .driver = {308 .name = "kirin-drm",309 .of_match_table = kirin_drm_dt_ids,310 },311};312 313drm_module_platform_driver(kirin_drm_platform_driver);314 315MODULE_AUTHOR("Xinliang Liu <xinliang.liu@linaro.org>");316MODULE_AUTHOR("Xinliang Liu <z.liuxinliang@hisilicon.com>");317MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");318MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver");319MODULE_LICENSE("GPL v2");320