1
2
3
4
5
6
7
8
9
10
11
12
13#include <drm/drmP.h>
14#include <drm/drm_atomic.h>
15#include <drm/drm_atomic_helper.h>
16#include <drm/drm_crtc.h>
17#include <drm/drm_crtc_helper.h>
18#include <linux/clk.h>
19#include <video/videomode.h>
20
21#include "malidp_drv.h"
22#include "malidp_hw.h"
23
24static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc,
25 const struct drm_display_mode *mode,
26 struct drm_display_mode *adjusted_mode)
27{
28 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
29 struct malidp_hw_device *hwdev = malidp->dev;
30
31
32
33
34
35 long rate, req_rate = mode->crtc_clock * 1000;
36
37 if (req_rate) {
38 rate = clk_round_rate(hwdev->mclk, req_rate);
39 if (rate < req_rate) {
40 DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n",
41 mode->crtc_clock);
42 return false;
43 }
44
45 rate = clk_round_rate(hwdev->pxlclk, req_rate);
46 if (rate != req_rate) {
47 DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n",
48 req_rate);
49 return false;
50 }
51 }
52
53 return true;
54}
55
56static void malidp_crtc_enable(struct drm_crtc *crtc)
57{
58 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
59 struct malidp_hw_device *hwdev = malidp->dev;
60 struct videomode vm;
61
62 drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm);
63
64 clk_prepare_enable(hwdev->pxlclk);
65
66
67 clk_set_rate(hwdev->mclk, crtc->state->adjusted_mode.crtc_clock * 1000);
68 clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000);
69
70 hwdev->modeset(hwdev, &vm);
71 hwdev->leave_config_mode(hwdev);
72 drm_crtc_vblank_on(crtc);
73}
74
75static void malidp_crtc_disable(struct drm_crtc *crtc)
76{
77 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
78 struct malidp_hw_device *hwdev = malidp->dev;
79
80 drm_crtc_vblank_off(crtc);
81 hwdev->enter_config_mode(hwdev);
82 clk_disable_unprepare(hwdev->pxlclk);
83}
84
85static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
86 struct drm_crtc_state *state)
87{
88 struct malidp_drm *malidp = crtc_to_malidp_device(crtc);
89 struct malidp_hw_device *hwdev = malidp->dev;
90 struct drm_plane *plane;
91 const struct drm_plane_state *pstate;
92 u32 rot_mem_free, rot_mem_usable;
93 int rotated_planes = 0;
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
121 if (pstate->rotation & MALIDP_ROTATED_MASK)
122 rotated_planes++;
123 }
124
125 rot_mem_free = hwdev->rotation_memory[0];
126
127
128
129
130 if (rotated_planes > 1)
131 rot_mem_free += hwdev->rotation_memory[1];
132
133
134 drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
135 struct malidp_plane *mp = to_malidp_plane(plane);
136 struct malidp_plane_state *ms = to_malidp_plane_state(pstate);
137
138 if (pstate->rotation & MALIDP_ROTATED_MASK) {
139
140 rotated_planes--;
141
142 if (!rotated_planes) {
143
144 rot_mem_usable = rot_mem_free;
145 } else {
146 if ((mp->layer->id != DE_VIDEO1) ||
147 (hwdev->rotation_memory[1] == 0))
148 rot_mem_usable = rot_mem_free / 2;
149 else
150 rot_mem_usable = hwdev->rotation_memory[0];
151 }
152
153 rot_mem_free -= rot_mem_usable;
154
155 if (ms->rotmem_size > rot_mem_usable)
156 return -EINVAL;
157 }
158 }
159
160 return 0;
161}
162
163static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = {
164 .mode_fixup = malidp_crtc_mode_fixup,
165 .enable = malidp_crtc_enable,
166 .disable = malidp_crtc_disable,
167 .atomic_check = malidp_crtc_atomic_check,
168};
169
170static const struct drm_crtc_funcs malidp_crtc_funcs = {
171 .destroy = drm_crtc_cleanup,
172 .set_config = drm_atomic_helper_set_config,
173 .page_flip = drm_atomic_helper_page_flip,
174 .reset = drm_atomic_helper_crtc_reset,
175 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
176 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
177};
178
179int malidp_crtc_init(struct drm_device *drm)
180{
181 struct malidp_drm *malidp = drm->dev_private;
182 struct drm_plane *primary = NULL, *plane;
183 int ret;
184
185 ret = malidp_de_planes_init(drm);
186 if (ret < 0) {
187 DRM_ERROR("Failed to initialise planes\n");
188 return ret;
189 }
190
191 drm_for_each_plane(plane, drm) {
192 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
193 primary = plane;
194 break;
195 }
196 }
197
198 if (!primary) {
199 DRM_ERROR("no primary plane found\n");
200 ret = -EINVAL;
201 goto crtc_cleanup_planes;
202 }
203
204 ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL,
205 &malidp_crtc_funcs, NULL);
206
207 if (!ret) {
208 drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs);
209 return 0;
210 }
211
212crtc_cleanup_planes:
213 malidp_de_planes_destroy(drm);
214
215 return ret;
216}
217