19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22048e328SMark Yao /*
32048e328SMark Yao * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
42048e328SMark Yao * Author:Mark Yao <mark.yao@rock-chips.com>
52048e328SMark Yao *
62048e328SMark Yao * based on exynos_drm_drv.c
72048e328SMark Yao */
82048e328SMark Yao
92048e328SMark Yao #include <linux/dma-mapping.h>
10*722d4f06SRob Herring #include <linux/platform_device.h>
112048e328SMark Yao #include <linux/pm_runtime.h>
1200fe6148SPaul Gortmaker #include <linux/module.h>
132048e328SMark Yao #include <linux/of_graph.h>
143880f62eSHeiko Stuebner #include <linux/of_platform.h>
152048e328SMark Yao #include <linux/component.h>
165a587383STomeu Vizoso #include <linux/console.h>
171aa5ca6eSShunqian Zheng #include <linux/iommu.h>
182048e328SMark Yao
193c1ed51aSJavier Martinez Canillas #include <drm/drm_aperture.h>
20c2156ccdSSam Ravnborg #include <drm/drm_drv.h>
218ab59da2SThomas Zimmermann #include <drm/drm_fbdev_generic.h>
224a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
23c2156ccdSSam Ravnborg #include <drm/drm_of.h>
24c2156ccdSSam Ravnborg #include <drm/drm_probe_helper.h>
25c2156ccdSSam Ravnborg #include <drm/drm_vblank.h>
26c2156ccdSSam Ravnborg
278490cad4SSteven Price #if defined(CONFIG_ARM_DMA_USE_IOMMU)
288490cad4SSteven Price #include <asm/dma-iommu.h>
298490cad4SSteven Price #else
308490cad4SSteven Price #define arm_iommu_detach_device(...) ({ })
318490cad4SSteven Price #define arm_iommu_release_mapping(...) ({ })
328490cad4SSteven Price #define to_dma_iommu_mapping(dev) NULL
338490cad4SSteven Price #endif
348490cad4SSteven Price
352048e328SMark Yao #include "rockchip_drm_drv.h"
362048e328SMark Yao #include "rockchip_drm_fb.h"
372048e328SMark Yao #include "rockchip_drm_gem.h"
382048e328SMark Yao
392048e328SMark Yao #define DRIVER_NAME "rockchip"
402048e328SMark Yao #define DRIVER_DESC "RockChip Soc DRM"
412048e328SMark Yao #define DRIVER_DATE "20140818"
422048e328SMark Yao #define DRIVER_MAJOR 1
432048e328SMark Yao #define DRIVER_MINOR 0
442048e328SMark Yao
4570a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver;
462d90d477SMark Yao
472048e328SMark Yao /*
482048e328SMark Yao * Attach a (component) device to the shared drm dma mapping from master drm
492048e328SMark Yao * device. This is used by the VOPs to map GEM buffers to a common DMA
502048e328SMark Yao * mapping.
512048e328SMark Yao */
rockchip_drm_dma_attach_device(struct drm_device * drm_dev,struct device * dev)522048e328SMark Yao int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
532048e328SMark Yao struct device *dev)
542048e328SMark Yao {
551aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private;
562048e328SMark Yao int ret;
572048e328SMark Yao
58421be3eeSRobin Murphy if (!private->domain)
592d90d477SMark Yao return 0;
602d90d477SMark Yao
618490cad4SSteven Price if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
628490cad4SSteven Price struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
638490cad4SSteven Price
648490cad4SSteven Price if (mapping) {
658490cad4SSteven Price arm_iommu_detach_device(dev);
668490cad4SSteven Price arm_iommu_release_mapping(mapping);
678490cad4SSteven Price }
688490cad4SSteven Price }
698490cad4SSteven Price
701aa5ca6eSShunqian Zheng ret = iommu_attach_device(private->domain, dev);
711aa5ca6eSShunqian Zheng if (ret) {
72d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
732048e328SMark Yao return ret;
741aa5ca6eSShunqian Zheng }
752048e328SMark Yao
761aa5ca6eSShunqian Zheng return 0;
772048e328SMark Yao }
782048e328SMark Yao
rockchip_drm_dma_detach_device(struct drm_device * drm_dev,struct device * dev)792048e328SMark Yao void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
802048e328SMark Yao struct device *dev)
812048e328SMark Yao {
821aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private;
831aa5ca6eSShunqian Zheng
84421be3eeSRobin Murphy if (!private->domain)
852d90d477SMark Yao return;
862d90d477SMark Yao
87421be3eeSRobin Murphy iommu_detach_device(private->domain, dev);
88421be3eeSRobin Murphy }
89421be3eeSRobin Murphy
rockchip_drm_dma_init_device(struct drm_device * drm_dev,struct device * dev)90421be3eeSRobin Murphy void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
91421be3eeSRobin Murphy struct device *dev)
92421be3eeSRobin Murphy {
93421be3eeSRobin Murphy struct rockchip_drm_private *private = drm_dev->dev_private;
94421be3eeSRobin Murphy
95421be3eeSRobin Murphy if (!device_iommu_mapped(dev))
96421be3eeSRobin Murphy private->iommu_dev = ERR_PTR(-ENODEV);
97421be3eeSRobin Murphy else if (!private->iommu_dev)
98421be3eeSRobin Murphy private->iommu_dev = dev;
992048e328SMark Yao }
1002048e328SMark Yao
rockchip_drm_init_iommu(struct drm_device * drm_dev)1011aa5ca6eSShunqian Zheng static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
1021aa5ca6eSShunqian Zheng {
1031aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private;
1041aa5ca6eSShunqian Zheng struct iommu_domain_geometry *geometry;
1051aa5ca6eSShunqian Zheng u64 start, end;
1061aa5ca6eSShunqian Zheng
107421be3eeSRobin Murphy if (IS_ERR_OR_NULL(private->iommu_dev))
1081aa5ca6eSShunqian Zheng return 0;
1091aa5ca6eSShunqian Zheng
110421be3eeSRobin Murphy private->domain = iommu_domain_alloc(private->iommu_dev->bus);
1111aa5ca6eSShunqian Zheng if (!private->domain)
1121aa5ca6eSShunqian Zheng return -ENOMEM;
1131aa5ca6eSShunqian Zheng
1141aa5ca6eSShunqian Zheng geometry = &private->domain->geometry;
1151aa5ca6eSShunqian Zheng start = geometry->aperture_start;
1161aa5ca6eSShunqian Zheng end = geometry->aperture_end;
1171aa5ca6eSShunqian Zheng
1181aa5ca6eSShunqian Zheng DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n",
1191aa5ca6eSShunqian Zheng start, end);
1201aa5ca6eSShunqian Zheng drm_mm_init(&private->mm, start, end - start + 1);
1211aa5ca6eSShunqian Zheng mutex_init(&private->mm_lock);
1221aa5ca6eSShunqian Zheng
1231aa5ca6eSShunqian Zheng return 0;
1241aa5ca6eSShunqian Zheng }
1251aa5ca6eSShunqian Zheng
rockchip_iommu_cleanup(struct drm_device * drm_dev)1261aa5ca6eSShunqian Zheng static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
1271aa5ca6eSShunqian Zheng {
1281aa5ca6eSShunqian Zheng struct rockchip_drm_private *private = drm_dev->dev_private;
1291aa5ca6eSShunqian Zheng
130421be3eeSRobin Murphy if (!private->domain)
1311aa5ca6eSShunqian Zheng return;
1321aa5ca6eSShunqian Zheng
1331aa5ca6eSShunqian Zheng drm_mm_takedown(&private->mm);
1341aa5ca6eSShunqian Zheng iommu_domain_free(private->domain);
1352048e328SMark Yao }
1362048e328SMark Yao
rockchip_drm_bind(struct device * dev)137f706974aSTomeu Vizoso static int rockchip_drm_bind(struct device *dev)
1382048e328SMark Yao {
139f706974aSTomeu Vizoso struct drm_device *drm_dev;
1402048e328SMark Yao struct rockchip_drm_private *private;
1412048e328SMark Yao int ret;
1422048e328SMark Yao
1433c1ed51aSJavier Martinez Canillas /* Remove existing drivers that may own the framebuffer memory. */
14462aeaeaaSDaniel Vetter ret = drm_aperture_remove_framebuffers(&rockchip_drm_driver);
1453c1ed51aSJavier Martinez Canillas if (ret) {
1463c1ed51aSJavier Martinez Canillas DRM_DEV_ERROR(dev,
1473c1ed51aSJavier Martinez Canillas "Failed to remove existing framebuffers - %d.\n",
1483c1ed51aSJavier Martinez Canillas ret);
1493c1ed51aSJavier Martinez Canillas return ret;
1503c1ed51aSJavier Martinez Canillas }
1513c1ed51aSJavier Martinez Canillas
152f706974aSTomeu Vizoso drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
1530f288605STom Gundersen if (IS_ERR(drm_dev))
1540f288605STom Gundersen return PTR_ERR(drm_dev);
1552048e328SMark Yao
156f706974aSTomeu Vizoso dev_set_drvdata(dev, drm_dev);
157f706974aSTomeu Vizoso
158f706974aSTomeu Vizoso private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
159f706974aSTomeu Vizoso if (!private) {
160f706974aSTomeu Vizoso ret = -ENOMEM;
1619127f99cSTomasz Figa goto err_free;
162f706974aSTomeu Vizoso }
163f706974aSTomeu Vizoso
1642048e328SMark Yao drm_dev->dev_private = private;
1652048e328SMark Yao
16656e35f85SDaniel Vetter ret = drmm_mode_config_init(drm_dev);
16756e35f85SDaniel Vetter if (ret)
168421be3eeSRobin Murphy goto err_free;
1692048e328SMark Yao
1702048e328SMark Yao rockchip_drm_mode_config_init(drm_dev);
1712048e328SMark Yao
1722048e328SMark Yao /* Try to bind all sub drivers. */
1732048e328SMark Yao ret = component_bind_all(dev, drm_dev);
1742048e328SMark Yao if (ret)
175421be3eeSRobin Murphy goto err_free;
176421be3eeSRobin Murphy
177421be3eeSRobin Murphy ret = rockchip_drm_init_iommu(drm_dev);
178421be3eeSRobin Murphy if (ret)
179421be3eeSRobin Murphy goto err_unbind_all;
1802048e328SMark Yao
181ccea9199SJeffy Chen ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
182ccea9199SJeffy Chen if (ret)
183421be3eeSRobin Murphy goto err_iommu_cleanup;
184ccea9199SJeffy Chen
185ccea9199SJeffy Chen drm_mode_config_reset(drm_dev);
1862048e328SMark Yao
187ccea9199SJeffy Chen /* init kms poll for handling hpd */
188ccea9199SJeffy Chen drm_kms_helper_poll_init(drm_dev);
18963ebb9faSMark Yao
1908415ab56SMark yao ret = drm_dev_register(drm_dev, 0);
1912048e328SMark Yao if (ret)
192ccea9199SJeffy Chen goto err_kms_helper_poll_fini;
1932048e328SMark Yao
194f2ed93a4SJohn Keeping drm_fbdev_generic_setup(drm_dev, 0);
19524af7c34SJohn Keeping
1962048e328SMark Yao return 0;
1972048e328SMark Yao err_kms_helper_poll_fini:
1982048e328SMark Yao drm_kms_helper_poll_fini(drm_dev);
19956e35f85SDaniel Vetter err_iommu_cleanup:
200ccea9199SJeffy Chen rockchip_iommu_cleanup(drm_dev);
201421be3eeSRobin Murphy err_unbind_all:
202421be3eeSRobin Murphy component_unbind_all(dev, drm_dev);
203f706974aSTomeu Vizoso err_free:
204574e0fbfSThomas Zimmermann drm_dev_put(drm_dev);
2052048e328SMark Yao return ret;
2062048e328SMark Yao }
2072048e328SMark Yao
rockchip_drm_unbind(struct device * dev)208f706974aSTomeu Vizoso static void rockchip_drm_unbind(struct device *dev)
2092048e328SMark Yao {
210f706974aSTomeu Vizoso struct drm_device *drm_dev = dev_get_drvdata(dev);
2112048e328SMark Yao
212f706974aSTomeu Vizoso drm_dev_unregister(drm_dev);
213ccea9199SJeffy Chen
214ccea9199SJeffy Chen drm_kms_helper_poll_fini(drm_dev);
215ccea9199SJeffy Chen
216c1bb8188SJeffy Chen drm_atomic_helper_shutdown(drm_dev);
217ccea9199SJeffy Chen component_unbind_all(dev, drm_dev);
218ccea9199SJeffy Chen rockchip_iommu_cleanup(drm_dev);
219ccea9199SJeffy Chen
220574e0fbfSThomas Zimmermann drm_dev_put(drm_dev);
2212048e328SMark Yao }
2222048e328SMark Yao
223f8b53070SThomas Zimmermann DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops);
2242048e328SMark Yao
22570a59dd8SDaniel Vetter static const struct drm_driver rockchip_drm_driver = {
2260424fdafSDaniel Vetter .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
2272048e328SMark Yao .dumb_create = rockchip_gem_dumb_create,
2286fd0bfe2SHaixia Shi .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
2292048e328SMark Yao .fops = &rockchip_drm_driver_fops,
2302048e328SMark Yao .name = DRIVER_NAME,
2312048e328SMark Yao .desc = DRIVER_DESC,
2322048e328SMark Yao .date = DRIVER_DATE,
2332048e328SMark Yao .major = DRIVER_MAJOR,
2342048e328SMark Yao .minor = DRIVER_MINOR,
2352048e328SMark Yao };
2362048e328SMark Yao
2372048e328SMark Yao #ifdef CONFIG_PM_SLEEP
rockchip_drm_sys_suspend(struct device * dev)2382048e328SMark Yao static int rockchip_drm_sys_suspend(struct device *dev)
2392048e328SMark Yao {
2402048e328SMark Yao struct drm_device *drm = dev_get_drvdata(dev);
2410fa375e6SJeffy Chen
242e7941cc2SSouptick Joarder return drm_mode_config_helper_suspend(drm);
2432048e328SMark Yao }
2442048e328SMark Yao
rockchip_drm_sys_resume(struct device * dev)2452048e328SMark Yao static int rockchip_drm_sys_resume(struct device *dev)
2462048e328SMark Yao {
2472048e328SMark Yao struct drm_device *drm = dev_get_drvdata(dev);
2482048e328SMark Yao
249e7941cc2SSouptick Joarder return drm_mode_config_helper_resume(drm);
2502048e328SMark Yao }
2512048e328SMark Yao #endif
2522048e328SMark Yao
2532048e328SMark Yao static const struct dev_pm_ops rockchip_drm_pm_ops = {
2542048e328SMark Yao SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
2552048e328SMark Yao rockchip_drm_sys_resume)
2562048e328SMark Yao };
2572048e328SMark Yao
2588820b68bSJeffy Chen #define MAX_ROCKCHIP_SUB_DRIVERS 16
2598820b68bSJeffy Chen static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS];
2608820b68bSJeffy Chen static int num_rockchip_sub_drivers;
2618820b68bSJeffy Chen
2623880f62eSHeiko Stuebner /*
263cf544c6aSSascha Hauer * Get the endpoint id of the remote endpoint of the given encoder. This
264cf544c6aSSascha Hauer * information is used by the VOP2 driver to identify the encoder.
265cf544c6aSSascha Hauer *
266cf544c6aSSascha Hauer * @rkencoder: The encoder to get the remote endpoint id from
267cf544c6aSSascha Hauer * @np: The encoder device node
268cf544c6aSSascha Hauer * @port: The number of the port leading to the VOP2
269cf544c6aSSascha Hauer * @reg: The endpoint number leading to the VOP2
270cf544c6aSSascha Hauer */
rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder * rkencoder,struct device_node * np,int port,int reg)271cf544c6aSSascha Hauer int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder,
272cf544c6aSSascha Hauer struct device_node *np, int port, int reg)
273cf544c6aSSascha Hauer {
274cf544c6aSSascha Hauer struct of_endpoint ep;
275cf544c6aSSascha Hauer struct device_node *en, *ren;
276cf544c6aSSascha Hauer int ret;
277cf544c6aSSascha Hauer
278cf544c6aSSascha Hauer en = of_graph_get_endpoint_by_regs(np, port, reg);
279cf544c6aSSascha Hauer if (!en)
280cf544c6aSSascha Hauer return -ENOENT;
281cf544c6aSSascha Hauer
282cf544c6aSSascha Hauer ren = of_graph_get_remote_endpoint(en);
283cf544c6aSSascha Hauer if (!ren)
284cf544c6aSSascha Hauer return -ENOENT;
285cf544c6aSSascha Hauer
286cf544c6aSSascha Hauer ret = of_graph_parse_endpoint(ren, &ep);
287cf544c6aSSascha Hauer if (ret)
288cf544c6aSSascha Hauer return ret;
289cf544c6aSSascha Hauer
290cf544c6aSSascha Hauer rkencoder->crtc_endpoint_id = ep.id;
291cf544c6aSSascha Hauer
292cf544c6aSSascha Hauer return 0;
293cf544c6aSSascha Hauer }
294cf544c6aSSascha Hauer
295cf544c6aSSascha Hauer /*
2963880f62eSHeiko Stuebner * Check if a vop endpoint is leading to a rockchip subdriver or bridge.
2973880f62eSHeiko Stuebner * Should be called from the component bind stage of the drivers
2983880f62eSHeiko Stuebner * to ensure that all subdrivers are probed.
2993880f62eSHeiko Stuebner *
3003880f62eSHeiko Stuebner * @ep: endpoint of a rockchip vop
3013880f62eSHeiko Stuebner *
3023880f62eSHeiko Stuebner * returns true if subdriver, false if external bridge and -ENODEV
3033880f62eSHeiko Stuebner * if remote port does not contain a device.
3043880f62eSHeiko Stuebner */
rockchip_drm_endpoint_is_subdriver(struct device_node * ep)3053880f62eSHeiko Stuebner int rockchip_drm_endpoint_is_subdriver(struct device_node *ep)
3063880f62eSHeiko Stuebner {
3073880f62eSHeiko Stuebner struct device_node *node = of_graph_get_remote_port_parent(ep);
3083880f62eSHeiko Stuebner struct platform_device *pdev;
3093880f62eSHeiko Stuebner struct device_driver *drv;
3103880f62eSHeiko Stuebner int i;
3113880f62eSHeiko Stuebner
3123880f62eSHeiko Stuebner if (!node)
3133880f62eSHeiko Stuebner return -ENODEV;
3143880f62eSHeiko Stuebner
3153880f62eSHeiko Stuebner /* status disabled will prevent creation of platform-devices */
31637825e07SAlex Bee if (!of_device_is_available(node)) {
31737825e07SAlex Bee of_node_put(node);
31837825e07SAlex Bee return -ENODEV;
31937825e07SAlex Bee }
32037825e07SAlex Bee
3213880f62eSHeiko Stuebner pdev = of_find_device_by_node(node);
3223880f62eSHeiko Stuebner of_node_put(node);
32337825e07SAlex Bee
32437825e07SAlex Bee /* enabled non-platform-devices can immediately return here */
3253880f62eSHeiko Stuebner if (!pdev)
32637825e07SAlex Bee return false;
3273880f62eSHeiko Stuebner
3283880f62eSHeiko Stuebner /*
3293880f62eSHeiko Stuebner * All rockchip subdrivers have probed at this point, so
3303880f62eSHeiko Stuebner * any device not having a driver now is an external bridge.
3313880f62eSHeiko Stuebner */
3323880f62eSHeiko Stuebner drv = pdev->dev.driver;
3333880f62eSHeiko Stuebner if (!drv) {
3343880f62eSHeiko Stuebner platform_device_put(pdev);
3353880f62eSHeiko Stuebner return false;
3363880f62eSHeiko Stuebner }
3373880f62eSHeiko Stuebner
3383880f62eSHeiko Stuebner for (i = 0; i < num_rockchip_sub_drivers; i++) {
3393880f62eSHeiko Stuebner if (rockchip_sub_drivers[i] == to_platform_driver(drv)) {
3403880f62eSHeiko Stuebner platform_device_put(pdev);
3413880f62eSHeiko Stuebner return true;
3423880f62eSHeiko Stuebner }
3433880f62eSHeiko Stuebner }
3443880f62eSHeiko Stuebner
3453880f62eSHeiko Stuebner platform_device_put(pdev);
3463880f62eSHeiko Stuebner return false;
3473880f62eSHeiko Stuebner }
3483880f62eSHeiko Stuebner
rockchip_drm_match_remove(struct device * dev)3497d4e981dSJeffy Chen static void rockchip_drm_match_remove(struct device *dev)
3507d4e981dSJeffy Chen {
3517d4e981dSJeffy Chen struct device_link *link;
3527d4e981dSJeffy Chen
3537d4e981dSJeffy Chen list_for_each_entry(link, &dev->links.consumers, s_node)
3547d4e981dSJeffy Chen device_link_del(link);
3557d4e981dSJeffy Chen }
3567d4e981dSJeffy Chen
rockchip_drm_match_add(struct device * dev)3578820b68bSJeffy Chen static struct component_match *rockchip_drm_match_add(struct device *dev)
3585bad7d29SMark Yao {
3598820b68bSJeffy Chen struct component_match *match = NULL;
3608820b68bSJeffy Chen int i;
3615bad7d29SMark Yao
3628820b68bSJeffy Chen for (i = 0; i < num_rockchip_sub_drivers; i++) {
3638820b68bSJeffy Chen struct platform_driver *drv = rockchip_sub_drivers[i];
3648820b68bSJeffy Chen struct device *p = NULL, *d;
3658820b68bSJeffy Chen
3668820b68bSJeffy Chen do {
36736f3313dSSuzuki K Poulose d = platform_find_device_by_driver(p, &drv->driver);
3688820b68bSJeffy Chen put_device(p);
3698820b68bSJeffy Chen p = d;
3708820b68bSJeffy Chen
3718820b68bSJeffy Chen if (!d)
3728820b68bSJeffy Chen break;
3737d4e981dSJeffy Chen
3747d4e981dSJeffy Chen device_link_add(dev, d, DL_FLAG_STATELESS);
375f798aa41SYong Wu component_match_add(dev, &match, component_compare_dev, d);
3768820b68bSJeffy Chen } while (true);
3775bad7d29SMark Yao }
3785bad7d29SMark Yao
3797d4e981dSJeffy Chen if (IS_ERR(match))
3807d4e981dSJeffy Chen rockchip_drm_match_remove(dev);
3817d4e981dSJeffy Chen
3828820b68bSJeffy Chen return match ?: ERR_PTR(-ENODEV);
3835bad7d29SMark Yao }
3845bad7d29SMark Yao
3852048e328SMark Yao static const struct component_master_ops rockchip_drm_ops = {
3862048e328SMark Yao .bind = rockchip_drm_bind,
3872048e328SMark Yao .unbind = rockchip_drm_unbind,
3882048e328SMark Yao };
3892048e328SMark Yao
rockchip_drm_platform_of_probe(struct device * dev)3908820b68bSJeffy Chen static int rockchip_drm_platform_of_probe(struct device *dev)
3912048e328SMark Yao {
3925bad7d29SMark Yao struct device_node *np = dev->of_node;
3935bad7d29SMark Yao struct device_node *port;
3948820b68bSJeffy Chen bool found = false;
3955bad7d29SMark Yao int i;
3962048e328SMark Yao
3975bad7d29SMark Yao if (!np)
3982048e328SMark Yao return -ENODEV;
3998820b68bSJeffy Chen
4005bad7d29SMark Yao for (i = 0;; i++) {
4015bad7d29SMark Yao port = of_parse_phandle(np, "ports", i);
4025bad7d29SMark Yao if (!port)
4035bad7d29SMark Yao break;
4042048e328SMark Yao
4055bad7d29SMark Yao if (!of_device_is_available(port->parent)) {
4065bad7d29SMark Yao of_node_put(port);
4075bad7d29SMark Yao continue;
4085bad7d29SMark Yao }
4095bad7d29SMark Yao
4108820b68bSJeffy Chen found = true;
4115bad7d29SMark Yao of_node_put(port);
4125bad7d29SMark Yao }
4135bad7d29SMark Yao
4145bad7d29SMark Yao if (i == 0) {
415d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev, "missing 'ports' property\n");
4165bad7d29SMark Yao return -ENODEV;
4175bad7d29SMark Yao }
4185bad7d29SMark Yao
4198820b68bSJeffy Chen if (!found) {
420d8dd6804SHaneen Mohammed DRM_DEV_ERROR(dev,
421d8dd6804SHaneen Mohammed "No available vop found for display-subsystem.\n");
4225bad7d29SMark Yao return -ENODEV;
4235bad7d29SMark Yao }
4245bad7d29SMark Yao
4258820b68bSJeffy Chen return 0;
4265bad7d29SMark Yao }
4275bad7d29SMark Yao
rockchip_drm_platform_probe(struct platform_device * pdev)4288820b68bSJeffy Chen static int rockchip_drm_platform_probe(struct platform_device *pdev)
4298820b68bSJeffy Chen {
4308820b68bSJeffy Chen struct device *dev = &pdev->dev;
4318820b68bSJeffy Chen struct component_match *match = NULL;
4328820b68bSJeffy Chen int ret;
4338820b68bSJeffy Chen
4348820b68bSJeffy Chen ret = rockchip_drm_platform_of_probe(dev);
4358820b68bSJeffy Chen if (ret)
4368820b68bSJeffy Chen return ret;
4378820b68bSJeffy Chen
4388820b68bSJeffy Chen match = rockchip_drm_match_add(dev);
4398820b68bSJeffy Chen if (IS_ERR(match))
4408820b68bSJeffy Chen return PTR_ERR(match);
4415bad7d29SMark Yao
4427d4e981dSJeffy Chen ret = component_master_add_with_match(dev, &rockchip_drm_ops, match);
4437d4e981dSJeffy Chen if (ret < 0) {
4447d4e981dSJeffy Chen rockchip_drm_match_remove(dev);
4457d4e981dSJeffy Chen return ret;
4467d4e981dSJeffy Chen }
4477d4e981dSJeffy Chen
4487d4e981dSJeffy Chen return 0;
4492048e328SMark Yao }
4502048e328SMark Yao
rockchip_drm_platform_remove(struct platform_device * pdev)4513c855610SUwe Kleine-König static void rockchip_drm_platform_remove(struct platform_device *pdev)
4522048e328SMark Yao {
4532048e328SMark Yao component_master_del(&pdev->dev, &rockchip_drm_ops);
4542048e328SMark Yao
4557d4e981dSJeffy Chen rockchip_drm_match_remove(&pdev->dev);
4562048e328SMark Yao }
4572048e328SMark Yao
rockchip_drm_platform_shutdown(struct platform_device * pdev)458b8f9d7f3SVicente Bergas static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
459b8f9d7f3SVicente Bergas {
460b8f9d7f3SVicente Bergas struct drm_device *drm = platform_get_drvdata(pdev);
461b8f9d7f3SVicente Bergas
462b8f9d7f3SVicente Bergas if (drm)
463b8f9d7f3SVicente Bergas drm_atomic_helper_shutdown(drm);
464b8f9d7f3SVicente Bergas }
465b8f9d7f3SVicente Bergas
4662048e328SMark Yao static const struct of_device_id rockchip_drm_dt_ids[] = {
4672048e328SMark Yao { .compatible = "rockchip,display-subsystem", },
4682048e328SMark Yao { /* sentinel */ },
4692048e328SMark Yao };
4702048e328SMark Yao MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
4712048e328SMark Yao
4722048e328SMark Yao static struct platform_driver rockchip_drm_platform_driver = {
4732048e328SMark Yao .probe = rockchip_drm_platform_probe,
4743c855610SUwe Kleine-König .remove_new = rockchip_drm_platform_remove,
475b8f9d7f3SVicente Bergas .shutdown = rockchip_drm_platform_shutdown,
4762048e328SMark Yao .driver = {
4772048e328SMark Yao .name = "rockchip-drm",
4782048e328SMark Yao .of_match_table = rockchip_drm_dt_ids,
4792048e328SMark Yao .pm = &rockchip_drm_pm_ops,
4802048e328SMark Yao },
4812048e328SMark Yao };
4822048e328SMark Yao
4838820b68bSJeffy Chen #define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \
4848820b68bSJeffy Chen if (IS_ENABLED(cond) && \
4858820b68bSJeffy Chen !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \
4868820b68bSJeffy Chen rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \
4878820b68bSJeffy Chen }
4888820b68bSJeffy Chen
rockchip_drm_init(void)4898820b68bSJeffy Chen static int __init rockchip_drm_init(void)
4908820b68bSJeffy Chen {
4918820b68bSJeffy Chen int ret;
4928820b68bSJeffy Chen
49309037781SJavier Martinez Canillas if (drm_firmware_drivers_only())
49409037781SJavier Martinez Canillas return -ENODEV;
49509037781SJavier Martinez Canillas
4968820b68bSJeffy Chen num_rockchip_sub_drivers = 0;
497b382406aSSascha Hauer ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
498604be855SAndy Yan ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
49934cc0aa2SSandy Huang ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
50034cc0aa2SSandy Huang CONFIG_ROCKCHIP_LVDS);
5018820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
5028820b68bSJeffy Chen CONFIG_ROCKCHIP_ANALOGIX_DP);
5038820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
5048820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
5058820b68bSJeffy Chen CONFIG_ROCKCHIP_DW_HDMI);
5062d4f7bdaSNickey Yang ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
5078820b68bSJeffy Chen CONFIG_ROCKCHIP_DW_MIPI_DSI);
5088820b68bSJeffy Chen ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
509f84d3d37SZheng Yang ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
510f84d3d37SZheng Yang CONFIG_ROCKCHIP_RK3066_HDMI);
5118820b68bSJeffy Chen
5128820b68bSJeffy Chen ret = platform_register_drivers(rockchip_sub_drivers,
5138820b68bSJeffy Chen num_rockchip_sub_drivers);
5148820b68bSJeffy Chen if (ret)
5158820b68bSJeffy Chen return ret;
5168820b68bSJeffy Chen
5178820b68bSJeffy Chen ret = platform_driver_register(&rockchip_drm_platform_driver);
5188820b68bSJeffy Chen if (ret)
5198820b68bSJeffy Chen goto err_unreg_drivers;
5208820b68bSJeffy Chen
5218820b68bSJeffy Chen return 0;
5228820b68bSJeffy Chen
5238820b68bSJeffy Chen err_unreg_drivers:
5248820b68bSJeffy Chen platform_unregister_drivers(rockchip_sub_drivers,
5258820b68bSJeffy Chen num_rockchip_sub_drivers);
5268820b68bSJeffy Chen return ret;
5278820b68bSJeffy Chen }
5288820b68bSJeffy Chen
rockchip_drm_fini(void)5298820b68bSJeffy Chen static void __exit rockchip_drm_fini(void)
5308820b68bSJeffy Chen {
5318820b68bSJeffy Chen platform_driver_unregister(&rockchip_drm_platform_driver);
5328820b68bSJeffy Chen
5338820b68bSJeffy Chen platform_unregister_drivers(rockchip_sub_drivers,
5348820b68bSJeffy Chen num_rockchip_sub_drivers);
5358820b68bSJeffy Chen }
5368820b68bSJeffy Chen
5378820b68bSJeffy Chen module_init(rockchip_drm_init);
5388820b68bSJeffy Chen module_exit(rockchip_drm_fini);
5392048e328SMark Yao
5402048e328SMark Yao MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
5412048e328SMark Yao MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
5422048e328SMark Yao MODULE_LICENSE("GPL v2");
543