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