]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
06c0dd96 | 2 | /* |
00453981 | 3 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
06c0dd96 RC |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> | |
06c0dd96 RC |
6 | */ |
7 | ||
78f27b1c | 8 | #include <drm/drm_crtc.h> |
fcd70cd3 | 9 | #include <drm/drm_probe_helper.h> |
06c0dd96 | 10 | |
78f27b1c | 11 | #include "mdp5_kms.h" |
06c0dd96 | 12 | |
06c0dd96 RC |
13 | static struct mdp5_kms *get_kms(struct drm_encoder *encoder) |
14 | { | |
15 | struct msm_drm_private *priv = encoder->dev->dev_private; | |
16 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
17 | } | |
18 | ||
6490ad47 | 19 | #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING |
06c0dd96 RC |
20 | #include <mach/board.h> |
21 | #include <mach/msm_bus.h> | |
22 | #include <mach/msm_bus_board.h> | |
23 | #define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ | |
24 | { \ | |
25 | .src = MSM_BUS_MASTER_MDP_PORT0, \ | |
26 | .dst = MSM_BUS_SLAVE_EBI_CH0, \ | |
27 | .ab = (ab_val), \ | |
28 | .ib = (ib_val), \ | |
29 | } | |
30 | ||
31 | static struct msm_bus_vectors mdp_bus_vectors[] = { | |
32 | MDP_BUS_VECTOR_ENTRY(0, 0), | |
33 | MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), | |
34 | }; | |
35 | static struct msm_bus_paths mdp_bus_usecases[] = { { | |
36 | .num_paths = 1, | |
37 | .vectors = &mdp_bus_vectors[0], | |
38 | }, { | |
39 | .num_paths = 1, | |
40 | .vectors = &mdp_bus_vectors[1], | |
41 | } }; | |
42 | static struct msm_bus_scale_pdata mdp_bus_scale_table = { | |
43 | .usecase = mdp_bus_usecases, | |
44 | .num_usecases = ARRAY_SIZE(mdp_bus_usecases), | |
45 | .name = "mdss_mdp", | |
46 | }; | |
47 | ||
48 | static void bs_init(struct mdp5_encoder *mdp5_encoder) | |
49 | { | |
50 | mdp5_encoder->bsc = msm_bus_scale_register_client( | |
51 | &mdp_bus_scale_table); | |
52 | DBG("bus scale client: %08x", mdp5_encoder->bsc); | |
53 | } | |
54 | ||
55 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) | |
56 | { | |
57 | if (mdp5_encoder->bsc) { | |
58 | msm_bus_scale_unregister_client(mdp5_encoder->bsc); | |
59 | mdp5_encoder->bsc = 0; | |
60 | } | |
61 | } | |
62 | ||
63 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) | |
64 | { | |
65 | if (mdp5_encoder->bsc) { | |
66 | DBG("set bus scaling: %d", idx); | |
67 | /* HACK: scaling down, and then immediately back up | |
68 | * seems to leave things broken (underflow).. so | |
69 | * never disable: | |
70 | */ | |
71 | idx = 1; | |
72 | msm_bus_scale_client_update_request(mdp5_encoder->bsc, idx); | |
73 | } | |
74 | } | |
75 | #else | |
76 | static void bs_init(struct mdp5_encoder *mdp5_encoder) {} | |
77 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) {} | |
78 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) {} | |
79 | #endif | |
80 | ||
81 | static void mdp5_encoder_destroy(struct drm_encoder *encoder) | |
82 | { | |
83 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
84 | bs_fini(mdp5_encoder); | |
85 | drm_encoder_cleanup(encoder); | |
86 | kfree(mdp5_encoder); | |
87 | } | |
88 | ||
89 | static const struct drm_encoder_funcs mdp5_encoder_funcs = { | |
90 | .destroy = mdp5_encoder_destroy, | |
91 | }; | |
92 | ||
df8a71d2 AT |
93 | static void mdp5_vid_encoder_mode_set(struct drm_encoder *encoder, |
94 | struct drm_display_mode *mode, | |
95 | struct drm_display_mode *adjusted_mode) | |
06c0dd96 RC |
96 | { |
97 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
98 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
00453981 HL |
99 | struct drm_device *dev = encoder->dev; |
100 | struct drm_connector *connector; | |
36d1364a | 101 | int intf = mdp5_encoder->intf->num; |
06c0dd96 RC |
102 | uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; |
103 | uint32_t display_v_start, display_v_end; | |
104 | uint32_t hsync_start_x, hsync_end_x; | |
00453981 | 105 | uint32_t format = 0x2100; |
0deed25b | 106 | unsigned long flags; |
06c0dd96 RC |
107 | |
108 | mode = adjusted_mode; | |
109 | ||
7510a9c6 | 110 | DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode)); |
06c0dd96 RC |
111 | |
112 | ctrl_pol = 0; | |
1efb92a3 HL |
113 | |
114 | /* DSI controller cannot handle active-low sync signals. */ | |
36d1364a | 115 | if (mdp5_encoder->intf->type != INTF_DSI) { |
1efb92a3 HL |
116 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) |
117 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; | |
118 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
119 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_VSYNC_LOW; | |
120 | } | |
06c0dd96 RC |
121 | /* probably need to get DATA_EN polarity from panel.. */ |
122 | ||
123 | dtv_hsync_skew = 0; /* get this from panel? */ | |
00453981 HL |
124 | |
125 | /* Get color format from panel, default is 8bpc */ | |
126 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
127 | if (connector->encoder == encoder) { | |
128 | switch (connector->display_info.bpc) { | |
129 | case 4: | |
130 | format |= 0; | |
131 | break; | |
132 | case 5: | |
133 | format |= 0x15; | |
134 | break; | |
135 | case 6: | |
136 | format |= 0x2A; | |
137 | break; | |
138 | case 8: | |
139 | default: | |
140 | format |= 0x3F; | |
141 | break; | |
142 | } | |
143 | break; | |
144 | } | |
145 | } | |
06c0dd96 RC |
146 | |
147 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
148 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
149 | ||
150 | vsync_period = mode->vtotal * mode->htotal; | |
151 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
152 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; | |
153 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; | |
154 | ||
00453981 HL |
155 | /* |
156 | * For edp only: | |
157 | * DISPLAY_V_START = (VBP * HCYCLE) + HBP | |
158 | * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP | |
159 | */ | |
36d1364a | 160 | if (mdp5_encoder->intf->type == INTF_eDP) { |
00453981 HL |
161 | display_v_start += mode->htotal - mode->hsync_start; |
162 | display_v_end -= mode->hsync_start - mode->hdisplay; | |
163 | } | |
164 | ||
0deed25b SV |
165 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); |
166 | ||
06c0dd96 RC |
167 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf), |
168 | MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
169 | MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal)); | |
170 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_PERIOD_F0(intf), vsync_period); | |
171 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_LEN_F0(intf), vsync_len); | |
172 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_HCTL(intf), | |
173 | MDP5_INTF_DISPLAY_HCTL_START(hsync_start_x) | | |
174 | MDP5_INTF_DISPLAY_HCTL_END(hsync_end_x)); | |
175 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VSTART_F0(intf), display_v_start); | |
176 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VEND_F0(intf), display_v_end); | |
177 | mdp5_write(mdp5_kms, REG_MDP5_INTF_BORDER_COLOR(intf), 0); | |
178 | mdp5_write(mdp5_kms, REG_MDP5_INTF_UNDERFLOW_COLOR(intf), 0xff); | |
179 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_SKEW(intf), dtv_hsync_skew); | |
180 | mdp5_write(mdp5_kms, REG_MDP5_INTF_POLARITY_CTL(intf), ctrl_pol); | |
181 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_HCTL(intf), | |
182 | MDP5_INTF_ACTIVE_HCTL_START(0) | | |
183 | MDP5_INTF_ACTIVE_HCTL_END(0)); | |
184 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VSTART_F0(intf), 0); | |
185 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0); | |
186 | mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format); | |
187 | mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3); /* frame+line? */ | |
0deed25b SV |
188 | |
189 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); | |
389b09a1 | 190 | |
f316b25a | 191 | mdp5_crtc_set_pipeline(encoder->crtc); |
06c0dd96 RC |
192 | } |
193 | ||
df8a71d2 | 194 | static void mdp5_vid_encoder_disable(struct drm_encoder *encoder) |
06c0dd96 | 195 | { |
0b776d45 RC |
196 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
197 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
c71716b1 | 198 | struct mdp5_ctl *ctl = mdp5_encoder->ctl; |
f316b25a | 199 | struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); |
adfc0e63 | 200 | struct mdp5_hw_mixer *mixer = mdp5_crtc_get_mixer(encoder->crtc); |
36d1364a AT |
201 | struct mdp5_interface *intf = mdp5_encoder->intf; |
202 | int intfn = mdp5_encoder->intf->num; | |
0b776d45 RC |
203 | unsigned long flags; |
204 | ||
205 | if (WARN_ON(!mdp5_encoder->enabled)) | |
206 | return; | |
207 | ||
f316b25a | 208 | mdp5_ctl_set_encoder_state(ctl, pipeline, false); |
389b09a1 | 209 | |
0b776d45 | 210 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); |
d145dd78 | 211 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 0); |
0b776d45 | 212 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); |
f9cb8d8d | 213 | mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true); |
0b776d45 RC |
214 | |
215 | /* | |
216 | * Wait for a vsync so we know the ENABLE=0 latched before | |
217 | * the (connector) source of the vsync's gets disabled, | |
218 | * otherwise we end up in a funny state if we re-enable | |
219 | * before the disable latches, which results that some of | |
220 | * the settings changes for the new modeset (like new | |
221 | * scanout buffer) don't latch properly.. | |
222 | */ | |
a2380124 | 223 | mdp_irq_wait(&mdp5_kms->base, intf2vblank(mixer, intf)); |
0b776d45 RC |
224 | |
225 | bs_set(mdp5_encoder, 0); | |
226 | ||
227 | mdp5_encoder->enabled = false; | |
06c0dd96 RC |
228 | } |
229 | ||
df8a71d2 | 230 | static void mdp5_vid_encoder_enable(struct drm_encoder *encoder) |
06c0dd96 RC |
231 | { |
232 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
0b776d45 | 233 | struct mdp5_kms *mdp5_kms = get_kms(encoder); |
c71716b1 | 234 | struct mdp5_ctl *ctl = mdp5_encoder->ctl; |
36d1364a | 235 | struct mdp5_interface *intf = mdp5_encoder->intf; |
f316b25a | 236 | struct mdp5_pipeline *pipeline = mdp5_crtc_get_pipeline(encoder->crtc); |
36d1364a | 237 | int intfn = intf->num; |
0b776d45 RC |
238 | unsigned long flags; |
239 | ||
240 | if (WARN_ON(mdp5_encoder->enabled)) | |
241 | return; | |
242 | ||
0b776d45 RC |
243 | bs_set(mdp5_encoder, 1); |
244 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); | |
d145dd78 | 245 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 1); |
0b776d45 | 246 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); |
f9cb8d8d | 247 | mdp5_ctl_commit(ctl, pipeline, mdp_ctl_flush_mask_encoder(intf), true); |
389b09a1 | 248 | |
f316b25a | 249 | mdp5_ctl_set_encoder_state(ctl, pipeline, true); |
0b776d45 | 250 | |
5db0f6e8 | 251 | mdp5_encoder->enabled = true; |
06c0dd96 RC |
252 | } |
253 | ||
df8a71d2 AT |
254 | static void mdp5_encoder_mode_set(struct drm_encoder *encoder, |
255 | struct drm_display_mode *mode, | |
256 | struct drm_display_mode *adjusted_mode) | |
257 | { | |
b3a94705 | 258 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
36d1364a | 259 | struct mdp5_interface *intf = mdp5_encoder->intf; |
b3a94705 AT |
260 | |
261 | if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) | |
262 | mdp5_cmd_encoder_mode_set(encoder, mode, adjusted_mode); | |
263 | else | |
264 | mdp5_vid_encoder_mode_set(encoder, mode, adjusted_mode); | |
df8a71d2 AT |
265 | } |
266 | ||
267 | static void mdp5_encoder_disable(struct drm_encoder *encoder) | |
268 | { | |
b3a94705 | 269 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
36d1364a | 270 | struct mdp5_interface *intf = mdp5_encoder->intf; |
b3a94705 AT |
271 | |
272 | if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) | |
273 | mdp5_cmd_encoder_disable(encoder); | |
274 | else | |
275 | mdp5_vid_encoder_disable(encoder); | |
df8a71d2 AT |
276 | } |
277 | ||
278 | static void mdp5_encoder_enable(struct drm_encoder *encoder) | |
279 | { | |
b3a94705 | 280 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
36d1364a | 281 | struct mdp5_interface *intf = mdp5_encoder->intf; |
710e7a44 AT |
282 | /* this isn't right I think */ |
283 | struct drm_crtc_state *cstate = encoder->crtc->state; | |
284 | ||
285 | mdp5_encoder_mode_set(encoder, &cstate->mode, &cstate->adjusted_mode); | |
b3a94705 AT |
286 | |
287 | if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) | |
b0e77fd8 | 288 | mdp5_cmd_encoder_enable(encoder); |
b3a94705 AT |
289 | else |
290 | mdp5_vid_encoder_enable(encoder); | |
df8a71d2 AT |
291 | } |
292 | ||
502e3550 AT |
293 | static int mdp5_encoder_atomic_check(struct drm_encoder *encoder, |
294 | struct drm_crtc_state *crtc_state, | |
295 | struct drm_connector_state *conn_state) | |
296 | { | |
297 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
298 | struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc_state); | |
299 | struct mdp5_interface *intf = mdp5_encoder->intf; | |
300 | struct mdp5_ctl *ctl = mdp5_encoder->ctl; | |
301 | ||
302 | mdp5_cstate->ctl = ctl; | |
303 | mdp5_cstate->pipeline.intf = intf; | |
6e8bed6a RC |
304 | |
305 | /* | |
306 | * This is a bit awkward, but we want to flush the CTL and hit the | |
307 | * START bit at most once for an atomic update. In the non-full- | |
308 | * modeset case, this is done from crtc->atomic_flush(), but that | |
309 | * is too early in the case of full modeset, in which case we | |
310 | * defer to encoder->enable(). But we need to *know* whether | |
311 | * encoder->enable() will be called to do this: | |
312 | */ | |
313 | if (drm_atomic_crtc_needs_modeset(crtc_state)) | |
314 | mdp5_cstate->defer_start = true; | |
502e3550 AT |
315 | |
316 | return 0; | |
317 | } | |
318 | ||
06c0dd96 | 319 | static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { |
5db0f6e8 SV |
320 | .disable = mdp5_encoder_disable, |
321 | .enable = mdp5_encoder_enable, | |
502e3550 | 322 | .atomic_check = mdp5_encoder_atomic_check, |
06c0dd96 RC |
323 | }; |
324 | ||
e2dd9f9f AT |
325 | int mdp5_encoder_get_linecount(struct drm_encoder *encoder) |
326 | { | |
327 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
328 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
36d1364a | 329 | int intf = mdp5_encoder->intf->num; |
e2dd9f9f AT |
330 | |
331 | return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf)); | |
332 | } | |
333 | ||
334 | u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder) | |
335 | { | |
336 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
337 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
36d1364a | 338 | int intf = mdp5_encoder->intf->num; |
e2dd9f9f AT |
339 | |
340 | return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf)); | |
341 | } | |
342 | ||
df8a71d2 AT |
343 | int mdp5_vid_encoder_set_split_display(struct drm_encoder *encoder, |
344 | struct drm_encoder *slave_encoder) | |
d5af49c9 HL |
345 | { |
346 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
b96b3a06 | 347 | struct mdp5_encoder *mdp5_slave_enc = to_mdp5_encoder(slave_encoder); |
d5af49c9 | 348 | struct mdp5_kms *mdp5_kms; |
d68fe15b | 349 | struct device *dev; |
d5af49c9 HL |
350 | int intf_num; |
351 | u32 data = 0; | |
352 | ||
353 | if (!encoder || !slave_encoder) | |
354 | return -EINVAL; | |
355 | ||
356 | mdp5_kms = get_kms(encoder); | |
36d1364a | 357 | intf_num = mdp5_encoder->intf->num; |
d5af49c9 HL |
358 | |
359 | /* Switch slave encoder's TimingGen Sync mode, | |
360 | * to use the master's enable signal for the slave encoder. | |
361 | */ | |
362 | if (intf_num == 1) | |
7b59c7e4 | 363 | data |= MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC; |
d5af49c9 | 364 | else if (intf_num == 2) |
7b59c7e4 | 365 | data |= MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC; |
d5af49c9 HL |
366 | else |
367 | return -EINVAL; | |
368 | ||
d68fe15b | 369 | dev = &mdp5_kms->pdev->dev; |
d5af49c9 | 370 | /* Make sure clocks are on when connectors calling this function. */ |
d68fe15b AT |
371 | pm_runtime_get_sync(dev); |
372 | ||
d5af49c9 | 373 | /* Dumb Panel, Sync mode */ |
7b59c7e4 AT |
374 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, 0); |
375 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, data); | |
376 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); | |
b96b3a06 HL |
377 | |
378 | mdp5_ctl_pair(mdp5_encoder->ctl, mdp5_slave_enc->ctl, true); | |
379 | ||
3c352b66 | 380 | pm_runtime_put_sync(dev); |
d5af49c9 HL |
381 | |
382 | return 0; | |
383 | } | |
384 | ||
9c9f6f8d AT |
385 | void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode) |
386 | { | |
387 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
36d1364a | 388 | struct mdp5_interface *intf = mdp5_encoder->intf; |
9c9f6f8d AT |
389 | |
390 | /* TODO: Expand this to set writeback modes too */ | |
391 | if (cmd_mode) { | |
392 | WARN_ON(intf->type != INTF_DSI); | |
393 | intf->mode = MDP5_INTF_DSI_MODE_COMMAND; | |
394 | } else { | |
395 | if (intf->type == INTF_DSI) | |
396 | intf->mode = MDP5_INTF_DSI_MODE_VIDEO; | |
397 | else | |
398 | intf->mode = MDP5_INTF_MODE_NONE; | |
399 | } | |
400 | } | |
401 | ||
06c0dd96 | 402 | /* initialize encoder */ |
d145dd78 | 403 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, |
36d1364a AT |
404 | struct mdp5_interface *intf, |
405 | struct mdp5_ctl *ctl) | |
06c0dd96 RC |
406 | { |
407 | struct drm_encoder *encoder = NULL; | |
408 | struct mdp5_encoder *mdp5_encoder; | |
d5af49c9 HL |
409 | int enc_type = (intf->type == INTF_DSI) ? |
410 | DRM_MODE_ENCODER_DSI : DRM_MODE_ENCODER_TMDS; | |
06c0dd96 RC |
411 | int ret; |
412 | ||
413 | mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL); | |
414 | if (!mdp5_encoder) { | |
415 | ret = -ENOMEM; | |
416 | goto fail; | |
417 | } | |
418 | ||
06c0dd96 | 419 | encoder = &mdp5_encoder->base; |
c71716b1 | 420 | mdp5_encoder->ctl = ctl; |
36d1364a | 421 | mdp5_encoder->intf = intf; |
06c0dd96 | 422 | |
0deed25b SV |
423 | spin_lock_init(&mdp5_encoder->intf_lock); |
424 | ||
13a3d91f | 425 | drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type, NULL); |
d5af49c9 | 426 | |
06c0dd96 RC |
427 | drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs); |
428 | ||
429 | bs_init(mdp5_encoder); | |
430 | ||
431 | return encoder; | |
432 | ||
433 | fail: | |
434 | if (encoder) | |
435 | mdp5_encoder_destroy(encoder); | |
436 | ||
437 | return ERR_PTR(ret); | |
438 | } |