1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include "drm/drm_bridge_connector.h" |
7 | |
8 | #include "msm_kms.h" |
9 | #include "dsi.h" |
10 | |
11 | #define DSI_CLOCK_MASTER DSI_0 |
12 | #define DSI_CLOCK_SLAVE DSI_1 |
13 | |
14 | #define DSI_LEFT DSI_0 |
15 | #define DSI_RIGHT DSI_1 |
16 | |
17 | /* According to the current drm framework sequence, take the encoder of |
18 | * DSI_1 as master encoder |
19 | */ |
20 | #define DSI_ENCODER_MASTER DSI_1 |
21 | #define DSI_ENCODER_SLAVE DSI_0 |
22 | |
23 | struct msm_dsi_manager { |
24 | struct msm_dsi *dsi[DSI_MAX]; |
25 | |
26 | bool is_bonded_dsi; |
27 | bool is_sync_needed; |
28 | int master_dsi_link_id; |
29 | }; |
30 | |
31 | static struct msm_dsi_manager msm_dsim_glb; |
32 | |
33 | #define IS_BONDED_DSI() (msm_dsim_glb.is_bonded_dsi) |
34 | #define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed) |
35 | #define IS_MASTER_DSI_LINK(id) (msm_dsim_glb.master_dsi_link_id == id) |
36 | |
37 | static inline struct msm_dsi *dsi_mgr_get_dsi(int id) |
38 | { |
39 | return msm_dsim_glb.dsi[id]; |
40 | } |
41 | |
42 | static inline struct msm_dsi *dsi_mgr_get_other_dsi(int id) |
43 | { |
44 | return msm_dsim_glb.dsi[(id + 1) % DSI_MAX]; |
45 | } |
46 | |
47 | static int dsi_mgr_parse_of(struct device_node *np, int id) |
48 | { |
49 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; |
50 | |
51 | /* We assume 2 dsi nodes have the same information of bonded dsi and |
52 | * sync-mode, and only one node specifies master in case of bonded mode. |
53 | */ |
54 | if (!msm_dsim->is_bonded_dsi) |
55 | msm_dsim->is_bonded_dsi = of_property_read_bool(np, "qcom,dual-dsi-mode" ); |
56 | |
57 | if (msm_dsim->is_bonded_dsi) { |
58 | if (of_property_read_bool(np, "qcom,master-dsi" )) |
59 | msm_dsim->master_dsi_link_id = id; |
60 | if (!msm_dsim->is_sync_needed) |
61 | msm_dsim->is_sync_needed = of_property_read_bool( |
62 | np, "qcom,sync-dual-dsi" ); |
63 | } |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static int dsi_mgr_setup_components(int id) |
69 | { |
70 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
71 | struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); |
72 | struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); |
73 | struct msm_dsi *clk_slave_dsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); |
74 | int ret; |
75 | |
76 | if (!IS_BONDED_DSI()) { |
77 | ret = msm_dsi_host_register(host: msm_dsi->host); |
78 | if (ret) |
79 | return ret; |
80 | |
81 | msm_dsi_phy_set_usecase(phy: msm_dsi->phy, uc: MSM_DSI_PHY_STANDALONE); |
82 | msm_dsi_host_set_phy_mode(host: msm_dsi->host, src_phy: msm_dsi->phy); |
83 | } else if (other_dsi) { |
84 | struct msm_dsi *master_link_dsi = IS_MASTER_DSI_LINK(id) ? |
85 | msm_dsi : other_dsi; |
86 | struct msm_dsi *slave_link_dsi = IS_MASTER_DSI_LINK(id) ? |
87 | other_dsi : msm_dsi; |
88 | /* Register slave host first, so that slave DSI device |
89 | * has a chance to probe, and do not block the master |
90 | * DSI device's probe. |
91 | * Also, do not check defer for the slave host, |
92 | * because only master DSI device adds the panel to global |
93 | * panel list. The panel's device is the master DSI device. |
94 | */ |
95 | ret = msm_dsi_host_register(host: slave_link_dsi->host); |
96 | if (ret) |
97 | return ret; |
98 | ret = msm_dsi_host_register(host: master_link_dsi->host); |
99 | if (ret) |
100 | return ret; |
101 | |
102 | /* PLL0 is to drive both 2 DSI link clocks in bonded DSI mode. */ |
103 | msm_dsi_phy_set_usecase(phy: clk_master_dsi->phy, |
104 | uc: MSM_DSI_PHY_MASTER); |
105 | msm_dsi_phy_set_usecase(phy: clk_slave_dsi->phy, |
106 | uc: MSM_DSI_PHY_SLAVE); |
107 | msm_dsi_host_set_phy_mode(host: msm_dsi->host, src_phy: msm_dsi->phy); |
108 | msm_dsi_host_set_phy_mode(host: other_dsi->host, src_phy: other_dsi->phy); |
109 | } |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int enable_phy(struct msm_dsi *msm_dsi, |
115 | struct msm_dsi_phy_shared_timings *shared_timings) |
116 | { |
117 | struct msm_dsi_phy_clk_request clk_req; |
118 | bool is_bonded_dsi = IS_BONDED_DSI(); |
119 | |
120 | msm_dsi_host_get_phy_clk_req(host: msm_dsi->host, clk_req: &clk_req, is_bonded_dsi); |
121 | |
122 | return msm_dsi_phy_enable(phy: msm_dsi->phy, clk_req: &clk_req, shared_timings); |
123 | } |
124 | |
125 | static int |
126 | dsi_mgr_phy_enable(int id, |
127 | struct msm_dsi_phy_shared_timings shared_timings[DSI_MAX]) |
128 | { |
129 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
130 | struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); |
131 | struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); |
132 | int ret; |
133 | |
134 | /* In case of bonded DSI, some registers in PHY1 have been programmed |
135 | * during PLL0 clock's set_rate. The PHY1 reset called by host1 here |
136 | * will silently reset those PHY1 registers. Therefore we need to reset |
137 | * and enable both PHYs before any PLL clock operation. |
138 | */ |
139 | if (IS_BONDED_DSI() && mdsi && sdsi) { |
140 | if (!mdsi->phy_enabled && !sdsi->phy_enabled) { |
141 | msm_dsi_host_reset_phy(host: mdsi->host); |
142 | msm_dsi_host_reset_phy(host: sdsi->host); |
143 | |
144 | ret = enable_phy(msm_dsi: mdsi, |
145 | shared_timings: &shared_timings[DSI_CLOCK_MASTER]); |
146 | if (ret) |
147 | return ret; |
148 | ret = enable_phy(msm_dsi: sdsi, |
149 | shared_timings: &shared_timings[DSI_CLOCK_SLAVE]); |
150 | if (ret) { |
151 | msm_dsi_phy_disable(phy: mdsi->phy); |
152 | return ret; |
153 | } |
154 | } |
155 | } else { |
156 | msm_dsi_host_reset_phy(host: msm_dsi->host); |
157 | ret = enable_phy(msm_dsi, shared_timings: &shared_timings[id]); |
158 | if (ret) |
159 | return ret; |
160 | } |
161 | |
162 | msm_dsi->phy_enabled = true; |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | static void dsi_mgr_phy_disable(int id) |
168 | { |
169 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
170 | struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); |
171 | struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); |
172 | |
173 | /* disable DSI phy |
174 | * In bonded dsi configuration, the phy should be disabled for the |
175 | * first controller only when the second controller is disabled. |
176 | */ |
177 | msm_dsi->phy_enabled = false; |
178 | if (IS_BONDED_DSI() && mdsi && sdsi) { |
179 | if (!mdsi->phy_enabled && !sdsi->phy_enabled) { |
180 | msm_dsi_phy_disable(phy: sdsi->phy); |
181 | msm_dsi_phy_disable(phy: mdsi->phy); |
182 | } |
183 | } else { |
184 | msm_dsi_phy_disable(phy: msm_dsi->phy); |
185 | } |
186 | } |
187 | |
188 | struct dsi_bridge { |
189 | struct drm_bridge base; |
190 | int id; |
191 | }; |
192 | |
193 | #define to_dsi_bridge(x) container_of(x, struct dsi_bridge, base) |
194 | |
195 | static int dsi_mgr_bridge_get_id(struct drm_bridge *bridge) |
196 | { |
197 | struct dsi_bridge *dsi_bridge = to_dsi_bridge(bridge); |
198 | return dsi_bridge->id; |
199 | } |
200 | |
201 | static int dsi_mgr_bridge_power_on(struct drm_bridge *bridge) |
202 | { |
203 | int id = dsi_mgr_bridge_get_id(bridge); |
204 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
205 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); |
206 | struct mipi_dsi_host *host = msm_dsi->host; |
207 | struct msm_dsi_phy_shared_timings phy_shared_timings[DSI_MAX]; |
208 | bool is_bonded_dsi = IS_BONDED_DSI(); |
209 | int ret; |
210 | |
211 | DBG("id=%d" , id); |
212 | |
213 | ret = dsi_mgr_phy_enable(id, shared_timings: phy_shared_timings); |
214 | if (ret) |
215 | goto phy_en_fail; |
216 | |
217 | ret = msm_dsi_host_power_on(host, phy_shared_timings: &phy_shared_timings[id], is_bonded_dsi, phy: msm_dsi->phy); |
218 | if (ret) { |
219 | pr_err("%s: power on host %d failed, %d\n" , __func__, id, ret); |
220 | goto host_on_fail; |
221 | } |
222 | |
223 | if (is_bonded_dsi && msm_dsi1) { |
224 | ret = msm_dsi_host_power_on(host: msm_dsi1->host, |
225 | phy_shared_timings: &phy_shared_timings[DSI_1], is_bonded_dsi, phy: msm_dsi1->phy); |
226 | if (ret) { |
227 | pr_err("%s: power on host1 failed, %d\n" , |
228 | __func__, ret); |
229 | goto host1_on_fail; |
230 | } |
231 | } |
232 | |
233 | /* |
234 | * Enable before preparing the panel, disable after unpreparing, so |
235 | * that the panel can communicate over the DSI link. |
236 | */ |
237 | msm_dsi_host_enable_irq(host); |
238 | if (is_bonded_dsi && msm_dsi1) |
239 | msm_dsi_host_enable_irq(host: msm_dsi1->host); |
240 | |
241 | return 0; |
242 | |
243 | host1_on_fail: |
244 | msm_dsi_host_power_off(host); |
245 | host_on_fail: |
246 | dsi_mgr_phy_disable(id); |
247 | phy_en_fail: |
248 | return ret; |
249 | } |
250 | |
251 | static void dsi_mgr_bridge_power_off(struct drm_bridge *bridge) |
252 | { |
253 | int id = dsi_mgr_bridge_get_id(bridge); |
254 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
255 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); |
256 | struct mipi_dsi_host *host = msm_dsi->host; |
257 | bool is_bonded_dsi = IS_BONDED_DSI(); |
258 | |
259 | msm_dsi_host_disable_irq(host); |
260 | if (is_bonded_dsi && msm_dsi1) { |
261 | msm_dsi_host_disable_irq(host: msm_dsi1->host); |
262 | msm_dsi_host_power_off(host: msm_dsi1->host); |
263 | } |
264 | msm_dsi_host_power_off(host); |
265 | dsi_mgr_phy_disable(id); |
266 | } |
267 | |
268 | static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) |
269 | { |
270 | int id = dsi_mgr_bridge_get_id(bridge); |
271 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
272 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); |
273 | struct mipi_dsi_host *host = msm_dsi->host; |
274 | bool is_bonded_dsi = IS_BONDED_DSI(); |
275 | int ret; |
276 | |
277 | DBG("id=%d" , id); |
278 | |
279 | /* Do nothing with the host if it is slave-DSI in case of bonded DSI */ |
280 | if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) |
281 | return; |
282 | |
283 | ret = dsi_mgr_bridge_power_on(bridge); |
284 | if (ret) { |
285 | dev_err(&msm_dsi->pdev->dev, "Power on failed: %d\n" , ret); |
286 | return; |
287 | } |
288 | |
289 | ret = msm_dsi_host_enable(host); |
290 | if (ret) { |
291 | pr_err("%s: enable host %d failed, %d\n" , __func__, id, ret); |
292 | goto host_en_fail; |
293 | } |
294 | |
295 | if (is_bonded_dsi && msm_dsi1) { |
296 | ret = msm_dsi_host_enable(host: msm_dsi1->host); |
297 | if (ret) { |
298 | pr_err("%s: enable host1 failed, %d\n" , __func__, ret); |
299 | goto host1_en_fail; |
300 | } |
301 | } |
302 | |
303 | return; |
304 | |
305 | host1_en_fail: |
306 | msm_dsi_host_disable(host); |
307 | host_en_fail: |
308 | dsi_mgr_bridge_power_off(bridge); |
309 | } |
310 | |
311 | void msm_dsi_manager_tpg_enable(void) |
312 | { |
313 | struct msm_dsi *m_dsi = dsi_mgr_get_dsi(DSI_0); |
314 | struct msm_dsi *s_dsi = dsi_mgr_get_dsi(DSI_1); |
315 | |
316 | /* if dual dsi, trigger tpg on master first then slave */ |
317 | if (m_dsi) { |
318 | msm_dsi_host_test_pattern_en(host: m_dsi->host); |
319 | if (IS_BONDED_DSI() && s_dsi) |
320 | msm_dsi_host_test_pattern_en(host: s_dsi->host); |
321 | } |
322 | } |
323 | |
324 | static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) |
325 | { |
326 | int id = dsi_mgr_bridge_get_id(bridge); |
327 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
328 | struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); |
329 | struct mipi_dsi_host *host = msm_dsi->host; |
330 | bool is_bonded_dsi = IS_BONDED_DSI(); |
331 | int ret; |
332 | |
333 | DBG("id=%d" , id); |
334 | |
335 | /* |
336 | * Do nothing with the host if it is slave-DSI in case of bonded DSI. |
337 | * It is safe to call dsi_mgr_phy_disable() here because a single PHY |
338 | * won't be diabled until both PHYs request disable. |
339 | */ |
340 | if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) |
341 | goto disable_phy; |
342 | |
343 | ret = msm_dsi_host_disable(host); |
344 | if (ret) |
345 | pr_err("%s: host %d disable failed, %d\n" , __func__, id, ret); |
346 | |
347 | if (is_bonded_dsi && msm_dsi1) { |
348 | ret = msm_dsi_host_disable(host: msm_dsi1->host); |
349 | if (ret) |
350 | pr_err("%s: host1 disable failed, %d\n" , __func__, ret); |
351 | } |
352 | |
353 | msm_dsi_host_disable_irq(host); |
354 | if (is_bonded_dsi && msm_dsi1) |
355 | msm_dsi_host_disable_irq(host: msm_dsi1->host); |
356 | |
357 | /* Save PHY status if it is a clock source */ |
358 | msm_dsi_phy_pll_save_state(phy: msm_dsi->phy); |
359 | |
360 | ret = msm_dsi_host_power_off(host); |
361 | if (ret) |
362 | pr_err("%s: host %d power off failed,%d\n" , __func__, id, ret); |
363 | |
364 | if (is_bonded_dsi && msm_dsi1) { |
365 | ret = msm_dsi_host_power_off(host: msm_dsi1->host); |
366 | if (ret) |
367 | pr_err("%s: host1 power off failed, %d\n" , |
368 | __func__, ret); |
369 | } |
370 | |
371 | disable_phy: |
372 | dsi_mgr_phy_disable(id); |
373 | } |
374 | |
375 | static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, |
376 | const struct drm_display_mode *mode, |
377 | const struct drm_display_mode *adjusted_mode) |
378 | { |
379 | int id = dsi_mgr_bridge_get_id(bridge); |
380 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
381 | struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); |
382 | struct mipi_dsi_host *host = msm_dsi->host; |
383 | bool is_bonded_dsi = IS_BONDED_DSI(); |
384 | |
385 | DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode)); |
386 | |
387 | if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id)) |
388 | return; |
389 | |
390 | msm_dsi_host_set_display_mode(host, mode: adjusted_mode); |
391 | if (is_bonded_dsi && other_dsi) |
392 | msm_dsi_host_set_display_mode(host: other_dsi->host, mode: adjusted_mode); |
393 | } |
394 | |
395 | static enum drm_mode_status dsi_mgr_bridge_mode_valid(struct drm_bridge *bridge, |
396 | const struct drm_display_info *info, |
397 | const struct drm_display_mode *mode) |
398 | { |
399 | int id = dsi_mgr_bridge_get_id(bridge); |
400 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
401 | struct mipi_dsi_host *host = msm_dsi->host; |
402 | struct platform_device *pdev = msm_dsi->pdev; |
403 | struct dev_pm_opp *opp; |
404 | unsigned long byte_clk_rate; |
405 | |
406 | byte_clk_rate = dsi_byte_clk_get_rate(host, IS_BONDED_DSI(), mode); |
407 | |
408 | opp = dev_pm_opp_find_freq_ceil(&pdev->dev, &byte_clk_rate); |
409 | if (!IS_ERR(ptr: opp)) { |
410 | dev_pm_opp_put(opp); |
411 | } else if (PTR_ERR(ptr: opp) == -ERANGE) { |
412 | /* |
413 | * An empty table is created by devm_pm_opp_set_clkname() even |
414 | * if there is none. Thus find_freq_ceil will still return |
415 | * -ERANGE in such case. |
416 | */ |
417 | if (dev_pm_opp_get_opp_count(&pdev->dev) != 0) |
418 | return MODE_CLOCK_RANGE; |
419 | } else { |
420 | return MODE_ERROR; |
421 | } |
422 | |
423 | return msm_dsi_host_check_dsc(host, mode); |
424 | } |
425 | |
426 | static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = { |
427 | .pre_enable = dsi_mgr_bridge_pre_enable, |
428 | .post_disable = dsi_mgr_bridge_post_disable, |
429 | .mode_set = dsi_mgr_bridge_mode_set, |
430 | .mode_valid = dsi_mgr_bridge_mode_valid, |
431 | }; |
432 | |
433 | /* initialize bridge */ |
434 | struct drm_bridge *msm_dsi_manager_bridge_init(struct msm_dsi *msm_dsi, |
435 | struct drm_encoder *encoder) |
436 | { |
437 | struct drm_bridge *bridge; |
438 | struct dsi_bridge *dsi_bridge; |
439 | int ret; |
440 | |
441 | dsi_bridge = devm_kzalloc(dev: msm_dsi->dev->dev, |
442 | size: sizeof(*dsi_bridge), GFP_KERNEL); |
443 | if (!dsi_bridge) |
444 | return ERR_PTR(error: -ENOMEM); |
445 | |
446 | dsi_bridge->id = msm_dsi->id; |
447 | |
448 | bridge = &dsi_bridge->base; |
449 | bridge->funcs = &dsi_mgr_bridge_funcs; |
450 | |
451 | ret = devm_drm_bridge_add(dev: msm_dsi->dev->dev, bridge); |
452 | if (ret) |
453 | return ERR_PTR(error: ret); |
454 | |
455 | ret = drm_bridge_attach(encoder, bridge, NULL, flags: 0); |
456 | if (ret) |
457 | return ERR_PTR(error: ret); |
458 | |
459 | return bridge; |
460 | } |
461 | |
462 | int msm_dsi_manager_ext_bridge_init(u8 id, struct drm_bridge *int_bridge) |
463 | { |
464 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
465 | struct drm_device *dev = msm_dsi->dev; |
466 | struct drm_encoder *encoder; |
467 | struct drm_bridge *ext_bridge; |
468 | int ret; |
469 | |
470 | ext_bridge = devm_drm_of_get_bridge(dev: &msm_dsi->pdev->dev, |
471 | node: msm_dsi->pdev->dev.of_node, port: 1, endpoint: 0); |
472 | if (IS_ERR(ptr: ext_bridge)) |
473 | return PTR_ERR(ptr: ext_bridge); |
474 | |
475 | encoder = int_bridge->encoder; |
476 | |
477 | /* |
478 | * Try first to create the bridge without it creating its own |
479 | * connector.. currently some bridges support this, and others |
480 | * do not (and some support both modes) |
481 | */ |
482 | ret = drm_bridge_attach(encoder, bridge: ext_bridge, previous: int_bridge, |
483 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
484 | if (ret == -EINVAL) { |
485 | /* |
486 | * link the internal dsi bridge to the external bridge, |
487 | * connector is created by the next bridge. |
488 | */ |
489 | ret = drm_bridge_attach(encoder, bridge: ext_bridge, previous: int_bridge, flags: 0); |
490 | if (ret < 0) |
491 | return ret; |
492 | } else { |
493 | struct drm_connector *connector; |
494 | |
495 | /* We are in charge of the connector, create one now. */ |
496 | connector = drm_bridge_connector_init(drm: dev, encoder); |
497 | if (IS_ERR(ptr: connector)) { |
498 | DRM_ERROR("Unable to create bridge connector\n" ); |
499 | return PTR_ERR(ptr: connector); |
500 | } |
501 | |
502 | ret = drm_connector_attach_encoder(connector, encoder); |
503 | if (ret < 0) |
504 | return ret; |
505 | } |
506 | |
507 | return 0; |
508 | } |
509 | |
510 | int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg) |
511 | { |
512 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
513 | struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0); |
514 | struct mipi_dsi_host *host = msm_dsi->host; |
515 | bool is_read = (msg->rx_buf && msg->rx_len); |
516 | bool need_sync = (IS_SYNC_NEEDED() && !is_read); |
517 | int ret; |
518 | |
519 | if (!msg->tx_buf || !msg->tx_len) |
520 | return 0; |
521 | |
522 | /* In bonded master case, panel requires the same commands sent to |
523 | * both DSI links. Host issues the command trigger to both links |
524 | * when DSI_1 calls the cmd transfer function, no matter it happens |
525 | * before or after DSI_0 cmd transfer. |
526 | */ |
527 | if (need_sync && (id == DSI_0)) |
528 | return is_read ? msg->rx_len : msg->tx_len; |
529 | |
530 | if (need_sync && msm_dsi0) { |
531 | ret = msm_dsi_host_xfer_prepare(host: msm_dsi0->host, msg); |
532 | if (ret) { |
533 | pr_err("%s: failed to prepare non-trigger host, %d\n" , |
534 | __func__, ret); |
535 | return ret; |
536 | } |
537 | } |
538 | ret = msm_dsi_host_xfer_prepare(host, msg); |
539 | if (ret) { |
540 | pr_err("%s: failed to prepare host, %d\n" , __func__, ret); |
541 | goto restore_host0; |
542 | } |
543 | |
544 | ret = is_read ? msm_dsi_host_cmd_rx(host, msg) : |
545 | msm_dsi_host_cmd_tx(host, msg); |
546 | |
547 | msm_dsi_host_xfer_restore(host, msg); |
548 | |
549 | restore_host0: |
550 | if (need_sync && msm_dsi0) |
551 | msm_dsi_host_xfer_restore(host: msm_dsi0->host, msg); |
552 | |
553 | return ret; |
554 | } |
555 | |
556 | bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len) |
557 | { |
558 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
559 | struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0); |
560 | struct mipi_dsi_host *host = msm_dsi->host; |
561 | |
562 | if (IS_SYNC_NEEDED() && (id == DSI_0)) |
563 | return false; |
564 | |
565 | if (IS_SYNC_NEEDED() && msm_dsi0) |
566 | msm_dsi_host_cmd_xfer_commit(host: msm_dsi0->host, dma_base, len); |
567 | |
568 | msm_dsi_host_cmd_xfer_commit(host, dma_base, len); |
569 | |
570 | return true; |
571 | } |
572 | |
573 | int msm_dsi_manager_register(struct msm_dsi *msm_dsi) |
574 | { |
575 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; |
576 | int id = msm_dsi->id; |
577 | int ret; |
578 | |
579 | if (id >= DSI_MAX) { |
580 | pr_err("%s: invalid id %d\n" , __func__, id); |
581 | return -EINVAL; |
582 | } |
583 | |
584 | if (msm_dsim->dsi[id]) { |
585 | pr_err("%s: dsi%d already registered\n" , __func__, id); |
586 | return -EBUSY; |
587 | } |
588 | |
589 | msm_dsim->dsi[id] = msm_dsi; |
590 | |
591 | ret = dsi_mgr_parse_of(np: msm_dsi->pdev->dev.of_node, id); |
592 | if (ret) { |
593 | pr_err("%s: failed to parse OF DSI info\n" , __func__); |
594 | goto fail; |
595 | } |
596 | |
597 | ret = dsi_mgr_setup_components(id); |
598 | if (ret) { |
599 | pr_err("%s: failed to register mipi dsi host for DSI %d: %d\n" , |
600 | __func__, id, ret); |
601 | goto fail; |
602 | } |
603 | |
604 | return 0; |
605 | |
606 | fail: |
607 | msm_dsim->dsi[id] = NULL; |
608 | return ret; |
609 | } |
610 | |
611 | void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi) |
612 | { |
613 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; |
614 | |
615 | if (msm_dsi->host) |
616 | msm_dsi_host_unregister(host: msm_dsi->host); |
617 | |
618 | if (msm_dsi->id >= 0) |
619 | msm_dsim->dsi[msm_dsi->id] = NULL; |
620 | } |
621 | |
622 | bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi) |
623 | { |
624 | return IS_BONDED_DSI(); |
625 | } |
626 | |
627 | bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi) |
628 | { |
629 | return IS_MASTER_DSI_LINK(msm_dsi->id); |
630 | } |
631 | |