Commit | Line | Data |
---|---|---|
a689554b HL |
1 | /* |
2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 and | |
6 | * only version 2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include "msm_kms.h" | |
15 | #include "dsi.h" | |
16 | ||
13351cd1 HL |
17 | #define DSI_CLOCK_MASTER DSI_0 |
18 | #define DSI_CLOCK_SLAVE DSI_1 | |
19 | ||
20 | #define DSI_LEFT DSI_0 | |
21 | #define DSI_RIGHT DSI_1 | |
22 | ||
23 | /* According to the current drm framework sequence, take the encoder of | |
24 | * DSI_1 as master encoder | |
25 | */ | |
26 | #define DSI_ENCODER_MASTER DSI_1 | |
27 | #define DSI_ENCODER_SLAVE DSI_0 | |
28 | ||
a689554b HL |
29 | struct msm_dsi_manager { |
30 | struct msm_dsi *dsi[DSI_MAX]; | |
31 | ||
32 | bool is_dual_panel; | |
33 | bool is_sync_needed; | |
34 | int master_panel_id; | |
35 | }; | |
36 | ||
37 | static struct msm_dsi_manager msm_dsim_glb; | |
38 | ||
39 | #define IS_DUAL_PANEL() (msm_dsim_glb.is_dual_panel) | |
40 | #define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed) | |
41 | #define IS_MASTER_PANEL(id) (msm_dsim_glb.master_panel_id == id) | |
42 | ||
43 | static inline struct msm_dsi *dsi_mgr_get_dsi(int id) | |
44 | { | |
45 | return msm_dsim_glb.dsi[id]; | |
46 | } | |
47 | ||
48 | static inline struct msm_dsi *dsi_mgr_get_other_dsi(int id) | |
49 | { | |
50 | return msm_dsim_glb.dsi[(id + 1) % DSI_MAX]; | |
51 | } | |
52 | ||
53 | static int dsi_mgr_parse_dual_panel(struct device_node *np, int id) | |
54 | { | |
55 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; | |
56 | ||
57 | /* We assume 2 dsi nodes have the same information of dual-panel and | |
58 | * sync-mode, and only one node specifies master in case of dual mode. | |
59 | */ | |
60 | if (!msm_dsim->is_dual_panel) | |
61 | msm_dsim->is_dual_panel = of_property_read_bool( | |
62 | np, "qcom,dual-panel-mode"); | |
63 | ||
64 | if (msm_dsim->is_dual_panel) { | |
65 | if (of_property_read_bool(np, "qcom,master-panel")) | |
66 | msm_dsim->master_panel_id = id; | |
67 | if (!msm_dsim->is_sync_needed) | |
68 | msm_dsim->is_sync_needed = of_property_read_bool( | |
69 | np, "qcom,sync-dual-panel"); | |
70 | } | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
9d32c498 HL |
75 | static int dsi_mgr_host_register(int id) |
76 | { | |
77 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
78 | struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); | |
79 | struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); | |
80 | struct msm_dsi_pll *src_pll; | |
81 | int ret; | |
82 | ||
83 | if (!IS_DUAL_PANEL()) { | |
84 | ret = msm_dsi_host_register(msm_dsi->host, true); | |
85 | if (ret) | |
86 | return ret; | |
87 | ||
88 | src_pll = msm_dsi_phy_get_pll(msm_dsi->phy); | |
89 | ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); | |
90 | } else if (!other_dsi) { | |
91 | ret = 0; | |
92 | } else { | |
93 | struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ? | |
94 | msm_dsi : other_dsi; | |
95 | struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ? | |
96 | other_dsi : msm_dsi; | |
97 | /* Register slave host first, so that slave DSI device | |
98 | * has a chance to probe, and do not block the master | |
99 | * DSI device's probe. | |
100 | * Also, do not check defer for the slave host, | |
101 | * because only master DSI device adds the panel to global | |
102 | * panel list. The panel's device is the master DSI device. | |
103 | */ | |
104 | ret = msm_dsi_host_register(sdsi->host, false); | |
105 | if (ret) | |
106 | return ret; | |
107 | ret = msm_dsi_host_register(mdsi->host, true); | |
108 | if (ret) | |
109 | return ret; | |
110 | ||
111 | /* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */ | |
112 | src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy); | |
113 | ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); | |
114 | if (ret) | |
115 | return ret; | |
116 | ret = msm_dsi_host_set_src_pll(other_dsi->host, src_pll); | |
117 | } | |
118 | ||
119 | return ret; | |
120 | } | |
121 | ||
a689554b HL |
122 | struct dsi_connector { |
123 | struct drm_connector base; | |
124 | int id; | |
125 | }; | |
126 | ||
127 | struct dsi_bridge { | |
128 | struct drm_bridge base; | |
129 | int id; | |
130 | }; | |
131 | ||
132 | #define to_dsi_connector(x) container_of(x, struct dsi_connector, base) | |
133 | #define to_dsi_bridge(x) container_of(x, struct dsi_bridge, base) | |
134 | ||
135 | static inline int dsi_mgr_connector_get_id(struct drm_connector *connector) | |
136 | { | |
137 | struct dsi_connector *dsi_connector = to_dsi_connector(connector); | |
138 | return dsi_connector->id; | |
139 | } | |
140 | ||
141 | static int dsi_mgr_bridge_get_id(struct drm_bridge *bridge) | |
142 | { | |
143 | struct dsi_bridge *dsi_bridge = to_dsi_bridge(bridge); | |
144 | return dsi_bridge->id; | |
145 | } | |
146 | ||
147 | static enum drm_connector_status dsi_mgr_connector_detect( | |
148 | struct drm_connector *connector, bool force) | |
149 | { | |
150 | int id = dsi_mgr_connector_get_id(connector); | |
151 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
152 | struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); | |
153 | struct msm_drm_private *priv = connector->dev->dev_private; | |
154 | struct msm_kms *kms = priv->kms; | |
155 | ||
156 | DBG("id=%d", id); | |
157 | if (!msm_dsi->panel) { | |
158 | msm_dsi->panel = msm_dsi_host_get_panel(msm_dsi->host, | |
159 | &msm_dsi->panel_flags); | |
160 | ||
161 | /* There is only 1 panel in the global panel list | |
162 | * for dual panel mode. Therefore slave dsi should get | |
163 | * the drm_panel instance from master dsi, and | |
164 | * keep using the panel flags got from the current DSI link. | |
165 | */ | |
166 | if (!msm_dsi->panel && IS_DUAL_PANEL() && | |
167 | !IS_MASTER_PANEL(id) && other_dsi) | |
168 | msm_dsi->panel = msm_dsi_host_get_panel( | |
169 | other_dsi->host, NULL); | |
170 | ||
171 | if (msm_dsi->panel && IS_DUAL_PANEL()) | |
172 | drm_object_attach_property(&connector->base, | |
173 | connector->dev->mode_config.tile_property, 0); | |
174 | ||
175 | /* Set split display info to kms once dual panel is connected | |
176 | * to both hosts | |
177 | */ | |
178 | if (msm_dsi->panel && IS_DUAL_PANEL() && | |
179 | other_dsi && other_dsi->panel) { | |
180 | bool cmd_mode = !(msm_dsi->panel_flags & | |
181 | MIPI_DSI_MODE_VIDEO); | |
182 | struct drm_encoder *encoder = msm_dsi_get_encoder( | |
183 | dsi_mgr_get_dsi(DSI_ENCODER_MASTER)); | |
184 | struct drm_encoder *slave_enc = msm_dsi_get_encoder( | |
185 | dsi_mgr_get_dsi(DSI_ENCODER_SLAVE)); | |
186 | ||
187 | if (kms->funcs->set_split_display) | |
188 | kms->funcs->set_split_display(kms, encoder, | |
189 | slave_enc, cmd_mode); | |
190 | else | |
191 | pr_err("mdp does not support dual panel\n"); | |
192 | } | |
193 | } | |
194 | ||
195 | return msm_dsi->panel ? connector_status_connected : | |
196 | connector_status_disconnected; | |
197 | } | |
198 | ||
199 | static void dsi_mgr_connector_destroy(struct drm_connector *connector) | |
200 | { | |
201 | DBG(""); | |
202 | drm_connector_unregister(connector); | |
203 | drm_connector_cleanup(connector); | |
204 | } | |
205 | ||
206 | static void dsi_dual_connector_fix_modes(struct drm_connector *connector) | |
207 | { | |
208 | struct drm_display_mode *mode, *m; | |
209 | ||
210 | /* Only support left-right mode */ | |
211 | list_for_each_entry_safe(mode, m, &connector->probed_modes, head) { | |
212 | mode->clock >>= 1; | |
213 | mode->hdisplay >>= 1; | |
214 | mode->hsync_start >>= 1; | |
215 | mode->hsync_end >>= 1; | |
216 | mode->htotal >>= 1; | |
217 | drm_mode_set_name(mode); | |
218 | } | |
219 | } | |
220 | ||
221 | static int dsi_dual_connector_tile_init( | |
222 | struct drm_connector *connector, int id) | |
223 | { | |
224 | struct drm_display_mode *mode; | |
225 | /* Fake topology id */ | |
226 | char topo_id[8] = {'M', 'S', 'M', 'D', 'U', 'D', 'S', 'I'}; | |
227 | ||
228 | if (connector->tile_group) { | |
229 | DBG("Tile property has been initialized"); | |
230 | return 0; | |
231 | } | |
232 | ||
233 | /* Use the first mode only for now */ | |
234 | mode = list_first_entry(&connector->probed_modes, | |
235 | struct drm_display_mode, | |
236 | head); | |
237 | if (!mode) | |
238 | return -EINVAL; | |
239 | ||
240 | connector->tile_group = drm_mode_get_tile_group( | |
241 | connector->dev, topo_id); | |
242 | if (!connector->tile_group) | |
243 | connector->tile_group = drm_mode_create_tile_group( | |
244 | connector->dev, topo_id); | |
245 | if (!connector->tile_group) { | |
246 | pr_err("%s: failed to create tile group\n", __func__); | |
247 | return -ENOMEM; | |
248 | } | |
249 | ||
250 | connector->has_tile = true; | |
251 | connector->tile_is_single_monitor = true; | |
252 | ||
253 | /* mode has been fixed */ | |
254 | connector->tile_h_size = mode->hdisplay; | |
255 | connector->tile_v_size = mode->vdisplay; | |
256 | ||
257 | /* Only support left-right mode */ | |
258 | connector->num_h_tile = 2; | |
259 | connector->num_v_tile = 1; | |
260 | ||
261 | connector->tile_v_loc = 0; | |
262 | connector->tile_h_loc = (id == DSI_RIGHT) ? 1 : 0; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static int dsi_mgr_connector_get_modes(struct drm_connector *connector) | |
268 | { | |
269 | int id = dsi_mgr_connector_get_id(connector); | |
270 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
271 | struct drm_panel *panel = msm_dsi->panel; | |
272 | int ret, num; | |
273 | ||
274 | if (!panel) | |
275 | return 0; | |
276 | ||
277 | /* Since we have 2 connectors, but only 1 drm_panel in dual DSI mode, | |
278 | * panel should not attach to any connector. | |
279 | * Only temporarily attach panel to the current connector here, | |
280 | * to let panel set mode to this connector. | |
281 | */ | |
282 | drm_panel_attach(panel, connector); | |
283 | num = drm_panel_get_modes(panel); | |
284 | drm_panel_detach(panel); | |
285 | if (!num) | |
286 | return 0; | |
287 | ||
288 | if (IS_DUAL_PANEL()) { | |
289 | /* report half resolution to user */ | |
290 | dsi_dual_connector_fix_modes(connector); | |
291 | ret = dsi_dual_connector_tile_init(connector, id); | |
292 | if (ret) | |
293 | return ret; | |
294 | ret = drm_mode_connector_set_tile_property(connector); | |
295 | if (ret) { | |
296 | pr_err("%s: set tile property failed, %d\n", | |
297 | __func__, ret); | |
298 | return ret; | |
299 | } | |
300 | } | |
301 | ||
302 | return num; | |
303 | } | |
304 | ||
305 | static int dsi_mgr_connector_mode_valid(struct drm_connector *connector, | |
306 | struct drm_display_mode *mode) | |
307 | { | |
308 | int id = dsi_mgr_connector_get_id(connector); | |
309 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
310 | struct drm_encoder *encoder = msm_dsi_get_encoder(msm_dsi); | |
311 | struct msm_drm_private *priv = connector->dev->dev_private; | |
312 | struct msm_kms *kms = priv->kms; | |
313 | long actual, requested; | |
314 | ||
315 | DBG(""); | |
316 | requested = 1000 * mode->clock; | |
317 | actual = kms->funcs->round_pixclk(kms, requested, encoder); | |
318 | ||
319 | DBG("requested=%ld, actual=%ld", requested, actual); | |
320 | if (actual != requested) | |
321 | return MODE_CLOCK_RANGE; | |
322 | ||
323 | return MODE_OK; | |
324 | } | |
325 | ||
326 | static struct drm_encoder * | |
327 | dsi_mgr_connector_best_encoder(struct drm_connector *connector) | |
328 | { | |
329 | int id = dsi_mgr_connector_get_id(connector); | |
330 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
331 | ||
332 | DBG(""); | |
333 | return msm_dsi_get_encoder(msm_dsi); | |
334 | } | |
335 | ||
336 | static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) | |
337 | { | |
338 | int id = dsi_mgr_bridge_get_id(bridge); | |
339 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
340 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); | |
341 | struct mipi_dsi_host *host = msm_dsi->host; | |
342 | struct drm_panel *panel = msm_dsi->panel; | |
343 | bool is_dual_panel = IS_DUAL_PANEL(); | |
344 | int ret; | |
345 | ||
346 | DBG("id=%d", id); | |
347 | if (!panel || (is_dual_panel && (DSI_1 == id))) | |
348 | return; | |
349 | ||
350 | ret = msm_dsi_host_power_on(host); | |
351 | if (ret) { | |
352 | pr_err("%s: power on host %d failed, %d\n", __func__, id, ret); | |
353 | goto host_on_fail; | |
354 | } | |
355 | ||
356 | if (is_dual_panel && msm_dsi1) { | |
357 | ret = msm_dsi_host_power_on(msm_dsi1->host); | |
358 | if (ret) { | |
359 | pr_err("%s: power on host1 failed, %d\n", | |
360 | __func__, ret); | |
361 | goto host1_on_fail; | |
362 | } | |
363 | } | |
364 | ||
365 | /* Always call panel functions once, because even for dual panels, | |
366 | * there is only one drm_panel instance. | |
367 | */ | |
368 | ret = drm_panel_prepare(panel); | |
369 | if (ret) { | |
370 | pr_err("%s: prepare panel %d failed, %d\n", __func__, id, ret); | |
371 | goto panel_prep_fail; | |
372 | } | |
373 | ||
374 | ret = msm_dsi_host_enable(host); | |
375 | if (ret) { | |
376 | pr_err("%s: enable host %d failed, %d\n", __func__, id, ret); | |
377 | goto host_en_fail; | |
378 | } | |
379 | ||
380 | if (is_dual_panel && msm_dsi1) { | |
381 | ret = msm_dsi_host_enable(msm_dsi1->host); | |
382 | if (ret) { | |
383 | pr_err("%s: enable host1 failed, %d\n", __func__, ret); | |
384 | goto host1_en_fail; | |
385 | } | |
386 | } | |
387 | ||
388 | ret = drm_panel_enable(panel); | |
389 | if (ret) { | |
390 | pr_err("%s: enable panel %d failed, %d\n", __func__, id, ret); | |
391 | goto panel_en_fail; | |
392 | } | |
393 | ||
394 | return; | |
395 | ||
396 | panel_en_fail: | |
397 | if (is_dual_panel && msm_dsi1) | |
398 | msm_dsi_host_disable(msm_dsi1->host); | |
399 | host1_en_fail: | |
400 | msm_dsi_host_disable(host); | |
401 | host_en_fail: | |
402 | drm_panel_unprepare(panel); | |
403 | panel_prep_fail: | |
404 | if (is_dual_panel && msm_dsi1) | |
405 | msm_dsi_host_power_off(msm_dsi1->host); | |
406 | host1_on_fail: | |
407 | msm_dsi_host_power_off(host); | |
408 | host_on_fail: | |
409 | return; | |
410 | } | |
411 | ||
412 | static void dsi_mgr_bridge_enable(struct drm_bridge *bridge) | |
413 | { | |
414 | DBG(""); | |
415 | } | |
416 | ||
417 | static void dsi_mgr_bridge_disable(struct drm_bridge *bridge) | |
418 | { | |
419 | DBG(""); | |
420 | } | |
421 | ||
422 | static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) | |
423 | { | |
424 | int id = dsi_mgr_bridge_get_id(bridge); | |
425 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
426 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); | |
427 | struct mipi_dsi_host *host = msm_dsi->host; | |
428 | struct drm_panel *panel = msm_dsi->panel; | |
429 | bool is_dual_panel = IS_DUAL_PANEL(); | |
430 | int ret; | |
431 | ||
432 | DBG("id=%d", id); | |
433 | ||
434 | if (!panel || (is_dual_panel && (DSI_1 == id))) | |
435 | return; | |
436 | ||
437 | ret = drm_panel_disable(panel); | |
438 | if (ret) | |
439 | pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, ret); | |
440 | ||
441 | ret = msm_dsi_host_disable(host); | |
442 | if (ret) | |
443 | pr_err("%s: host %d disable failed, %d\n", __func__, id, ret); | |
444 | ||
445 | if (is_dual_panel && msm_dsi1) { | |
446 | ret = msm_dsi_host_disable(msm_dsi1->host); | |
447 | if (ret) | |
448 | pr_err("%s: host1 disable failed, %d\n", __func__, ret); | |
449 | } | |
450 | ||
451 | ret = drm_panel_unprepare(panel); | |
452 | if (ret) | |
453 | pr_err("%s: Panel %d unprepare failed,%d\n", __func__, id, ret); | |
454 | ||
455 | ret = msm_dsi_host_power_off(host); | |
456 | if (ret) | |
457 | pr_err("%s: host %d power off failed,%d\n", __func__, id, ret); | |
458 | ||
459 | if (is_dual_panel && msm_dsi1) { | |
460 | ret = msm_dsi_host_power_off(msm_dsi1->host); | |
461 | if (ret) | |
462 | pr_err("%s: host1 power off failed, %d\n", | |
463 | __func__, ret); | |
464 | } | |
465 | } | |
466 | ||
467 | static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, | |
468 | struct drm_display_mode *mode, | |
469 | struct drm_display_mode *adjusted_mode) | |
470 | { | |
471 | int id = dsi_mgr_bridge_get_id(bridge); | |
472 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
473 | struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); | |
474 | struct mipi_dsi_host *host = msm_dsi->host; | |
475 | bool is_dual_panel = IS_DUAL_PANEL(); | |
476 | ||
477 | DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
478 | mode->base.id, mode->name, | |
479 | mode->vrefresh, mode->clock, | |
480 | mode->hdisplay, mode->hsync_start, | |
481 | mode->hsync_end, mode->htotal, | |
482 | mode->vdisplay, mode->vsync_start, | |
483 | mode->vsync_end, mode->vtotal, | |
484 | mode->type, mode->flags); | |
485 | ||
486 | if (is_dual_panel && (DSI_1 == id)) | |
487 | return; | |
488 | ||
489 | msm_dsi_host_set_display_mode(host, adjusted_mode); | |
490 | if (is_dual_panel && other_dsi) | |
491 | msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode); | |
492 | } | |
493 | ||
494 | static const struct drm_connector_funcs dsi_mgr_connector_funcs = { | |
495 | .dpms = drm_atomic_helper_connector_dpms, | |
496 | .detect = dsi_mgr_connector_detect, | |
497 | .fill_modes = drm_helper_probe_single_connector_modes, | |
498 | .destroy = dsi_mgr_connector_destroy, | |
499 | .reset = drm_atomic_helper_connector_reset, | |
500 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
501 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
502 | }; | |
503 | ||
504 | static const struct drm_connector_helper_funcs dsi_mgr_conn_helper_funcs = { | |
505 | .get_modes = dsi_mgr_connector_get_modes, | |
506 | .mode_valid = dsi_mgr_connector_mode_valid, | |
507 | .best_encoder = dsi_mgr_connector_best_encoder, | |
508 | }; | |
509 | ||
510 | static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = { | |
511 | .pre_enable = dsi_mgr_bridge_pre_enable, | |
512 | .enable = dsi_mgr_bridge_enable, | |
513 | .disable = dsi_mgr_bridge_disable, | |
514 | .post_disable = dsi_mgr_bridge_post_disable, | |
515 | .mode_set = dsi_mgr_bridge_mode_set, | |
516 | }; | |
517 | ||
518 | /* initialize connector */ | |
519 | struct drm_connector *msm_dsi_manager_connector_init(u8 id) | |
520 | { | |
521 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
522 | struct drm_connector *connector = NULL; | |
523 | struct dsi_connector *dsi_connector; | |
6f6b2879 | 524 | int ret, i; |
a689554b HL |
525 | |
526 | dsi_connector = devm_kzalloc(msm_dsi->dev->dev, | |
527 | sizeof(*dsi_connector), GFP_KERNEL); | |
528 | if (!dsi_connector) { | |
529 | ret = -ENOMEM; | |
530 | goto fail; | |
531 | } | |
532 | ||
533 | dsi_connector->id = id; | |
534 | ||
535 | connector = &dsi_connector->base; | |
536 | ||
537 | ret = drm_connector_init(msm_dsi->dev, connector, | |
538 | &dsi_mgr_connector_funcs, DRM_MODE_CONNECTOR_DSI); | |
539 | if (ret) | |
540 | goto fail; | |
541 | ||
542 | drm_connector_helper_add(connector, &dsi_mgr_conn_helper_funcs); | |
543 | ||
544 | /* Enable HPD to let hpd event is handled | |
545 | * when panel is attached to the host. | |
546 | */ | |
547 | connector->polled = DRM_CONNECTOR_POLL_HPD; | |
548 | ||
549 | /* Display driver doesn't support interlace now. */ | |
550 | connector->interlace_allowed = 0; | |
551 | connector->doublescan_allowed = 0; | |
552 | ||
553 | ret = drm_connector_register(connector); | |
554 | if (ret) | |
555 | goto fail; | |
556 | ||
6f6b2879 HL |
557 | for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) |
558 | drm_mode_connector_attach_encoder(connector, | |
559 | msm_dsi->encoders[i]); | |
560 | ||
a689554b HL |
561 | return connector; |
562 | ||
563 | fail: | |
564 | if (connector) | |
565 | dsi_mgr_connector_destroy(connector); | |
566 | ||
567 | return ERR_PTR(ret); | |
568 | } | |
569 | ||
570 | /* initialize bridge */ | |
571 | struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) | |
572 | { | |
573 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
574 | struct drm_bridge *bridge = NULL; | |
575 | struct dsi_bridge *dsi_bridge; | |
576 | int ret; | |
577 | ||
578 | dsi_bridge = devm_kzalloc(msm_dsi->dev->dev, | |
579 | sizeof(*dsi_bridge), GFP_KERNEL); | |
580 | if (!dsi_bridge) { | |
581 | ret = -ENOMEM; | |
582 | goto fail; | |
583 | } | |
584 | ||
585 | dsi_bridge->id = id; | |
586 | ||
587 | bridge = &dsi_bridge->base; | |
588 | bridge->funcs = &dsi_mgr_bridge_funcs; | |
589 | ||
590 | ret = drm_bridge_attach(msm_dsi->dev, bridge); | |
591 | if (ret) | |
592 | goto fail; | |
593 | ||
594 | return bridge; | |
595 | ||
596 | fail: | |
597 | if (bridge) | |
598 | msm_dsi_manager_bridge_destroy(bridge); | |
599 | ||
600 | return ERR_PTR(ret); | |
601 | } | |
602 | ||
603 | void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge) | |
604 | { | |
605 | } | |
606 | ||
607 | int msm_dsi_manager_phy_enable(int id, | |
608 | const unsigned long bit_rate, const unsigned long esc_rate, | |
609 | u32 *clk_pre, u32 *clk_post) | |
610 | { | |
611 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
612 | struct msm_dsi_phy *phy = msm_dsi->phy; | |
13351cd1 | 613 | int src_pll_id = IS_DUAL_PANEL() ? DSI_CLOCK_MASTER : id; |
a689554b HL |
614 | int ret; |
615 | ||
13351cd1 | 616 | ret = msm_dsi_phy_enable(phy, src_pll_id, bit_rate, esc_rate); |
a689554b HL |
617 | if (ret) |
618 | return ret; | |
619 | ||
620 | msm_dsi->phy_enabled = true; | |
621 | msm_dsi_phy_get_clk_pre_post(phy, clk_pre, clk_post); | |
622 | ||
623 | return 0; | |
624 | } | |
625 | ||
626 | void msm_dsi_manager_phy_disable(int id) | |
627 | { | |
628 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
629 | struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); | |
630 | struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); | |
631 | struct msm_dsi_phy *phy = msm_dsi->phy; | |
632 | ||
633 | /* disable DSI phy | |
634 | * In dual-dsi configuration, the phy should be disabled for the | |
635 | * first controller only when the second controller is disabled. | |
636 | */ | |
637 | msm_dsi->phy_enabled = false; | |
638 | if (IS_DUAL_PANEL() && mdsi && sdsi) { | |
639 | if (!mdsi->phy_enabled && !sdsi->phy_enabled) { | |
640 | msm_dsi_phy_disable(sdsi->phy); | |
641 | msm_dsi_phy_disable(mdsi->phy); | |
642 | } | |
643 | } else { | |
644 | msm_dsi_phy_disable(phy); | |
645 | } | |
646 | } | |
647 | ||
648 | int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg) | |
649 | { | |
650 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
651 | struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0); | |
652 | struct mipi_dsi_host *host = msm_dsi->host; | |
653 | bool is_read = (msg->rx_buf && msg->rx_len); | |
654 | bool need_sync = (IS_SYNC_NEEDED() && !is_read); | |
655 | int ret; | |
656 | ||
657 | if (!msg->tx_buf || !msg->tx_len) | |
658 | return 0; | |
659 | ||
660 | /* In dual master case, panel requires the same commands sent to | |
661 | * both DSI links. Host issues the command trigger to both links | |
662 | * when DSI_1 calls the cmd transfer function, no matter it happens | |
663 | * before or after DSI_0 cmd transfer. | |
664 | */ | |
665 | if (need_sync && (id == DSI_0)) | |
666 | return is_read ? msg->rx_len : msg->tx_len; | |
667 | ||
668 | if (need_sync && msm_dsi0) { | |
669 | ret = msm_dsi_host_xfer_prepare(msm_dsi0->host, msg); | |
670 | if (ret) { | |
671 | pr_err("%s: failed to prepare non-trigger host, %d\n", | |
672 | __func__, ret); | |
673 | return ret; | |
674 | } | |
675 | } | |
676 | ret = msm_dsi_host_xfer_prepare(host, msg); | |
677 | if (ret) { | |
678 | pr_err("%s: failed to prepare host, %d\n", __func__, ret); | |
679 | goto restore_host0; | |
680 | } | |
681 | ||
682 | ret = is_read ? msm_dsi_host_cmd_rx(host, msg) : | |
683 | msm_dsi_host_cmd_tx(host, msg); | |
684 | ||
685 | msm_dsi_host_xfer_restore(host, msg); | |
686 | ||
687 | restore_host0: | |
688 | if (need_sync && msm_dsi0) | |
689 | msm_dsi_host_xfer_restore(msm_dsi0->host, msg); | |
690 | ||
691 | return ret; | |
692 | } | |
693 | ||
694 | bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len) | |
695 | { | |
696 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); | |
697 | struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0); | |
698 | struct mipi_dsi_host *host = msm_dsi->host; | |
699 | ||
700 | if (IS_SYNC_NEEDED() && (id == DSI_0)) | |
701 | return false; | |
702 | ||
703 | if (IS_SYNC_NEEDED() && msm_dsi0) | |
704 | msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, iova, len); | |
705 | ||
706 | msm_dsi_host_cmd_xfer_commit(host, iova, len); | |
707 | ||
708 | return true; | |
709 | } | |
710 | ||
711 | int msm_dsi_manager_register(struct msm_dsi *msm_dsi) | |
712 | { | |
713 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; | |
714 | int id = msm_dsi->id; | |
a689554b HL |
715 | int ret; |
716 | ||
717 | if (id > DSI_MAX) { | |
718 | pr_err("%s: invalid id %d\n", __func__, id); | |
719 | return -EINVAL; | |
720 | } | |
721 | ||
722 | if (msm_dsim->dsi[id]) { | |
723 | pr_err("%s: dsi%d already registered\n", __func__, id); | |
724 | return -EBUSY; | |
725 | } | |
726 | ||
727 | msm_dsim->dsi[id] = msm_dsi; | |
728 | ||
729 | ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id); | |
730 | if (ret) { | |
731 | pr_err("%s: failed to parse dual panel info\n", __func__); | |
9d32c498 | 732 | goto fail; |
a689554b HL |
733 | } |
734 | ||
9d32c498 HL |
735 | ret = dsi_mgr_host_register(id); |
736 | if (ret) { | |
737 | pr_err("%s: failed to register mipi dsi host for DSI %d\n", | |
738 | __func__, id); | |
739 | goto fail; | |
a689554b HL |
740 | } |
741 | ||
9d32c498 HL |
742 | return 0; |
743 | ||
744 | fail: | |
745 | msm_dsim->dsi[id] = NULL; | |
a689554b HL |
746 | return ret; |
747 | } | |
748 | ||
749 | void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi) | |
750 | { | |
751 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; | |
752 | ||
753 | if (msm_dsi->host) | |
754 | msm_dsi_host_unregister(msm_dsi->host); | |
755 | msm_dsim->dsi[msm_dsi->id] = NULL; | |
756 | } | |
757 |