1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4  * Author: James.Qian.Wang <james.qian.wang@arm.com>
5  *
6  */
7 #include <linux/component.h>
8 #include <linux/interrupt.h>
9 
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_drv.h>
13 #include <drm/drm_fb_helper.h>
14 #include <drm/drm_gem_cma_helper.h>
15 #include <drm/drm_gem_framebuffer_helper.h>
16 #include <drm/drm_vblank.h>
17 
18 #include "komeda_dev.h"
19 #include "komeda_framebuffer.h"
20 #include "komeda_kms.h"
21 
22 DEFINE_DRM_GEM_CMA_FOPS(komeda_cma_fops);
23 
24 static int komeda_gem_cma_dumb_create(struct drm_file *file,
25 				      struct drm_device *dev,
26 				      struct drm_mode_create_dumb *args)
27 {
28 	u32 alignment = 16; /* TODO get alignment from dev */
29 
30 	args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8),
31 			    alignment);
32 
33 	return drm_gem_cma_dumb_create_internal(file, dev, args);
34 }
35 
36 static struct drm_driver komeda_kms_driver = {
37 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
38 			   DRIVER_PRIME,
39 	.lastclose			= drm_fb_helper_lastclose,
40 	.gem_free_object_unlocked	= drm_gem_cma_free_object,
41 	.gem_vm_ops			= &drm_gem_cma_vm_ops,
42 	.dumb_create			= komeda_gem_cma_dumb_create,
43 	.prime_handle_to_fd		= drm_gem_prime_handle_to_fd,
44 	.prime_fd_to_handle		= drm_gem_prime_fd_to_handle,
45 	.gem_prime_export		= drm_gem_prime_export,
46 	.gem_prime_import		= drm_gem_prime_import,
47 	.gem_prime_get_sg_table		= drm_gem_cma_prime_get_sg_table,
48 	.gem_prime_import_sg_table	= drm_gem_cma_prime_import_sg_table,
49 	.gem_prime_vmap			= drm_gem_cma_prime_vmap,
50 	.gem_prime_vunmap		= drm_gem_cma_prime_vunmap,
51 	.gem_prime_mmap			= drm_gem_cma_prime_mmap,
52 	.fops = &komeda_cma_fops,
53 	.name = "komeda",
54 	.desc = "Arm Komeda Display Processor driver",
55 	.date = "20181101",
56 	.major = 0,
57 	.minor = 1,
58 };
59 
60 static void komeda_kms_commit_tail(struct drm_atomic_state *old_state)
61 {
62 	struct drm_device *dev = old_state->dev;
63 
64 	drm_atomic_helper_commit_modeset_disables(dev, old_state);
65 
66 	drm_atomic_helper_commit_planes(dev, old_state, 0);
67 
68 	drm_atomic_helper_commit_modeset_enables(dev, old_state);
69 
70 	drm_atomic_helper_wait_for_flip_done(dev, old_state);
71 
72 	drm_atomic_helper_commit_hw_done(old_state);
73 
74 	drm_atomic_helper_cleanup_planes(dev, old_state);
75 }
76 
77 static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = {
78 	.atomic_commit_tail = komeda_kms_commit_tail,
79 };
80 
81 static const struct drm_mode_config_funcs komeda_mode_config_funcs = {
82 	.fb_create		= komeda_fb_create,
83 	.atomic_check		= drm_atomic_helper_check,
84 	.atomic_commit		= drm_atomic_helper_commit,
85 };
86 
87 static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms,
88 					struct komeda_dev *mdev)
89 {
90 	struct drm_mode_config *config = &kms->base.mode_config;
91 
92 	drm_mode_config_init(&kms->base);
93 
94 	komeda_kms_setup_crtcs(kms, mdev);
95 
96 	/* Get value from dev */
97 	config->min_width	= 0;
98 	config->min_height	= 0;
99 	config->max_width	= 4096;
100 	config->max_height	= 4096;
101 	config->allow_fb_modifiers = false;
102 
103 	config->funcs = &komeda_mode_config_funcs;
104 	config->helper_private = &komeda_mode_config_helpers;
105 }
106 
107 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
108 {
109 	struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL);
110 	struct drm_device *drm;
111 	int err;
112 
113 	if (!kms)
114 		return ERR_PTR(-ENOMEM);
115 
116 	drm = &kms->base;
117 	err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev);
118 	if (err)
119 		goto free_kms;
120 
121 	drm->dev_private = mdev;
122 
123 	komeda_kms_mode_config_init(kms, mdev);
124 
125 	err = komeda_kms_add_private_objs(kms, mdev);
126 	if (err)
127 		goto cleanup_mode_config;
128 
129 	err = komeda_kms_add_planes(kms, mdev);
130 	if (err)
131 		goto cleanup_mode_config;
132 
133 	err = drm_vblank_init(drm, kms->n_crtcs);
134 	if (err)
135 		goto cleanup_mode_config;
136 
137 	err = komeda_kms_add_crtcs(kms, mdev);
138 	if (err)
139 		goto cleanup_mode_config;
140 
141 	err = component_bind_all(mdev->dev, kms);
142 	if (err)
143 		goto cleanup_mode_config;
144 
145 	drm_mode_config_reset(drm);
146 
147 	err = drm_dev_register(drm, 0);
148 	if (err)
149 		goto cleanup_mode_config;
150 
151 	return kms;
152 
153 cleanup_mode_config:
154 	drm_mode_config_cleanup(drm);
155 free_kms:
156 	kfree(kms);
157 	return ERR_PTR(err);
158 }
159 
160 void komeda_kms_detach(struct komeda_kms_dev *kms)
161 {
162 	struct drm_device *drm = &kms->base;
163 	struct komeda_dev *mdev = drm->dev_private;
164 
165 	drm_dev_unregister(drm);
166 	component_unbind_all(mdev->dev, drm);
167 	komeda_kms_cleanup_private_objs(mdev);
168 	drm_mode_config_cleanup(drm);
169 	drm->dev_private = NULL;
170 	drm_dev_put(drm);
171 }
172