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