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