blob: 5b4786596d331590bfa77ff36a7f48ea9956f7d8 [file] [log] [blame]
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -07001/*
Ray Zhanga8a5dfe2018-01-22 10:29:01 +08002 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -07003 *
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
15
16#define pr_fmt(fmt) "dsi-drm:[%s] " fmt, __func__
17#include <drm/drm_atomic_helper.h>
18#include <drm/drm_atomic.h>
19
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070020#include "msm_kms.h"
Clarence Ip40d7d592016-07-15 16:02:26 -040021#include "sde_connector.h"
22#include "dsi_drm.h"
Narendra Muppalla77b32932017-05-10 13:53:11 -070023#include "sde_trace.h"
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070024
25#define to_dsi_bridge(x) container_of((x), struct dsi_bridge, base)
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070026#define to_dsi_state(x) container_of((x), struct dsi_connector_state, base)
27
28static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode,
29 struct dsi_display_mode *dsi_mode)
30{
Ajay Singh Parmar1181d272016-07-21 11:12:23 -070031 memset(dsi_mode, 0, sizeof(*dsi_mode));
32
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070033 dsi_mode->timing.h_active = drm_mode->hdisplay;
34 dsi_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
35 dsi_mode->timing.h_sync_width = drm_mode->htotal -
36 (drm_mode->hsync_start + dsi_mode->timing.h_back_porch);
37 dsi_mode->timing.h_front_porch = drm_mode->hsync_start -
38 drm_mode->hdisplay;
39 dsi_mode->timing.h_skew = drm_mode->hskew;
40
41 dsi_mode->timing.v_active = drm_mode->vdisplay;
42 dsi_mode->timing.v_back_porch = drm_mode->vtotal - drm_mode->vsync_end;
43 dsi_mode->timing.v_sync_width = drm_mode->vtotal -
44 (drm_mode->vsync_start + dsi_mode->timing.v_back_porch);
45
46 dsi_mode->timing.v_front_porch = drm_mode->vsync_start -
47 drm_mode->vdisplay;
48
49 dsi_mode->timing.refresh_rate = drm_mode->vrefresh;
50
51 dsi_mode->pixel_clk_khz = drm_mode->clock;
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070052
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -070053 dsi_mode->priv_info =
54 (struct dsi_display_mode_priv_info *)drm_mode->private;
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -070055
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070056 if (msm_is_mode_seamless(drm_mode))
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -050057 dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_SEAMLESS;
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070058 if (msm_is_mode_dynamic_fps(drm_mode))
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -050059 dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DFPS;
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070060 if (msm_needs_vblank_pre_modeset(drm_mode))
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -050061 dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VBLANK_PRE_MODESET;
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -070062 if (msm_is_mode_seamless_dms(drm_mode))
63 dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS;
Raviteja Tamatam68892de2017-06-20 04:47:19 +053064 if (msm_is_mode_seamless_vrr(drm_mode))
65 dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
Ray Zhanga8a5dfe2018-01-22 10:29:01 +080066
67 dsi_mode->timing.h_sync_polarity =
68 !!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC);
69 dsi_mode->timing.v_sync_polarity =
70 !!(drm_mode->flags & DRM_MODE_FLAG_PVSYNC);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070071}
72
Chandan Uddaraju3f2cf422017-06-15 15:37:39 -070073void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070074 struct drm_display_mode *drm_mode)
75{
Ajay Singh Parmar1181d272016-07-21 11:12:23 -070076 memset(drm_mode, 0, sizeof(*drm_mode));
77
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -070078 drm_mode->hdisplay = dsi_mode->timing.h_active;
79 drm_mode->hsync_start = drm_mode->hdisplay +
80 dsi_mode->timing.h_front_porch;
81 drm_mode->hsync_end = drm_mode->hsync_start +
82 dsi_mode->timing.h_sync_width;
83 drm_mode->htotal = drm_mode->hsync_end + dsi_mode->timing.h_back_porch;
84 drm_mode->hskew = dsi_mode->timing.h_skew;
85
86 drm_mode->vdisplay = dsi_mode->timing.v_active;
87 drm_mode->vsync_start = drm_mode->vdisplay +
88 dsi_mode->timing.v_front_porch;
89 drm_mode->vsync_end = drm_mode->vsync_start +
90 dsi_mode->timing.v_sync_width;
91 drm_mode->vtotal = drm_mode->vsync_end + dsi_mode->timing.v_back_porch;
92
93 drm_mode->vrefresh = dsi_mode->timing.refresh_rate;
94 drm_mode->clock = dsi_mode->pixel_clk_khz;
95
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -070096 drm_mode->private = (int *)dsi_mode->priv_info;
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -070097
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -050098 if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS)
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -070099 drm_mode->flags |= DRM_MODE_FLAG_SEAMLESS;
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -0500100 if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DFPS)
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -0700101 drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS;
Lloyd Atkinson7d12ce02016-12-13 11:32:57 -0500102 if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VBLANK_PRE_MODESET)
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -0700103 drm_mode->private_flags |= MSM_MODE_FLAG_VBLANK_PRE_MODESET;
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700104 if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS)
105 drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DMS;
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530106 if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR)
107 drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR;
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -0700108
Ray Zhanga8a5dfe2018-01-22 10:29:01 +0800109 if (dsi_mode->timing.h_sync_polarity)
110 drm_mode->flags |= DRM_MODE_FLAG_PHSYNC;
111 if (dsi_mode->timing.v_sync_polarity)
112 drm_mode->flags |= DRM_MODE_FLAG_PVSYNC;
113
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700114 drm_mode_set_name(drm_mode);
115}
116
117static int dsi_bridge_attach(struct drm_bridge *bridge)
118{
119 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
120
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400121 if (!bridge) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700122 pr_err("Invalid params\n");
123 return -EINVAL;
124 }
125
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700126 pr_debug("[%d] attached\n", c_bridge->id);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700127
128 return 0;
129
130}
131
132static void dsi_bridge_pre_enable(struct drm_bridge *bridge)
133{
134 int rc = 0;
135 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
136
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400137 if (!bridge) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700138 pr_err("Invalid params\n");
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700139 return;
140 }
141
Shashank Babu Chinta Venkata7d608732017-05-31 14:10:26 -0700142 if (!c_bridge || !c_bridge->display)
143 pr_err("Incorrect bridge details\n");
144
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700145 /* By this point mode should have been validated through mode_fixup */
146 rc = dsi_display_set_mode(c_bridge->display,
147 &(c_bridge->dsi_mode), 0x0);
148 if (rc) {
149 pr_err("[%d] failed to perform a mode set, rc=%d\n",
150 c_bridge->id, rc);
151 return;
152 }
153
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530154 if (c_bridge->dsi_mode.dsi_mode_flags &
155 (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700156 pr_debug("[%d] seamless pre-enable\n", c_bridge->id);
157 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700158 }
159
Narendra Muppalla77b32932017-05-10 13:53:11 -0700160 SDE_ATRACE_BEGIN("dsi_bridge_pre_enable");
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700161 rc = dsi_display_prepare(c_bridge->display);
162 if (rc) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700163 pr_err("[%d] DSI display prepare failed, rc=%d\n",
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700164 c_bridge->id, rc);
Narendra Muppalla77b32932017-05-10 13:53:11 -0700165 SDE_ATRACE_END("dsi_bridge_pre_enable");
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700166 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700167 }
168
Narendra Muppalla77b32932017-05-10 13:53:11 -0700169 SDE_ATRACE_BEGIN("dsi_display_enable");
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700170 rc = dsi_display_enable(c_bridge->display);
171 if (rc) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700172 pr_err("[%d] DSI display enable failed, rc=%d\n",
Shashank Babu Chinta Venkata7d608732017-05-31 14:10:26 -0700173 c_bridge->id, rc);
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700174 (void)dsi_display_unprepare(c_bridge->display);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700175 }
Narendra Muppalla77b32932017-05-10 13:53:11 -0700176 SDE_ATRACE_END("dsi_display_enable");
177 SDE_ATRACE_END("dsi_bridge_pre_enable");
Shashank Babu Chinta Venkata7d608732017-05-31 14:10:26 -0700178
179 rc = dsi_display_splash_res_cleanup(c_bridge->display);
180 if (rc)
181 pr_err("Continuous splash pipeline cleanup failed, rc=%d\n",
182 rc);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700183}
184
185static void dsi_bridge_enable(struct drm_bridge *bridge)
186{
187 int rc = 0;
188 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
189
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400190 if (!bridge) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700191 pr_err("Invalid params\n");
192 return;
193 }
194
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530195 if (c_bridge->dsi_mode.dsi_mode_flags &
196 (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700197 pr_debug("[%d] seamless enable\n", c_bridge->id);
198 return;
199 }
200
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700201 rc = dsi_display_post_enable(c_bridge->display);
202 if (rc)
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700203 pr_err("[%d] DSI display post enabled failed, rc=%d\n",
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700204 c_bridge->id, rc);
205}
206
207static void dsi_bridge_disable(struct drm_bridge *bridge)
208{
209 int rc = 0;
Clarence Ip458a5d02017-11-27 18:15:16 -0500210 struct dsi_display *display;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700211 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
212
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400213 if (!bridge) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700214 pr_err("Invalid params\n");
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400215 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700216 }
Clarence Ip458a5d02017-11-27 18:15:16 -0500217 display = c_bridge->display;
218
219 if (display && display->drm_conn)
220 sde_connector_helper_bridge_disable(display->drm_conn);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700221
222 rc = dsi_display_pre_disable(c_bridge->display);
223 if (rc) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700224 pr_err("[%d] DSI display pre disable failed, rc=%d\n",
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700225 c_bridge->id, rc);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700226 }
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700227}
228
229static void dsi_bridge_post_disable(struct drm_bridge *bridge)
230{
231 int rc = 0;
232 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
233
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400234 if (!bridge) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700235 pr_err("Invalid params\n");
236 return;
237 }
238
Narendra Muppalla77b32932017-05-10 13:53:11 -0700239 SDE_ATRACE_BEGIN("dsi_bridge_post_disable");
240 SDE_ATRACE_BEGIN("dsi_display_disable");
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700241 rc = dsi_display_disable(c_bridge->display);
242 if (rc) {
243 pr_err("[%d] DSI display disable failed, rc=%d\n",
244 c_bridge->id, rc);
Narendra Muppalla77b32932017-05-10 13:53:11 -0700245 SDE_ATRACE_END("dsi_display_disable");
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400246 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700247 }
Narendra Muppalla77b32932017-05-10 13:53:11 -0700248 SDE_ATRACE_END("dsi_display_disable");
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700249
250 rc = dsi_display_unprepare(c_bridge->display);
251 if (rc) {
252 pr_err("[%d] DSI display unprepare failed, rc=%d\n",
253 c_bridge->id, rc);
Narendra Muppalla77b32932017-05-10 13:53:11 -0700254 SDE_ATRACE_END("dsi_bridge_post_disable");
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400255 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700256 }
Narendra Muppalla77b32932017-05-10 13:53:11 -0700257 SDE_ATRACE_END("dsi_bridge_post_disable");
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700258}
259
260static void dsi_bridge_mode_set(struct drm_bridge *bridge,
261 struct drm_display_mode *mode,
262 struct drm_display_mode *adjusted_mode)
263{
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700264 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700265
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400266 if (!bridge || !mode || !adjusted_mode) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700267 pr_err("Invalid params\n");
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700268 return;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700269 }
270
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700271 memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode));
272 convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode));
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700273}
274
275static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge,
276 const struct drm_display_mode *mode,
277 struct drm_display_mode *adjusted_mode)
278{
279 int rc = 0;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700280 struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500281 struct dsi_display *display;
282 struct dsi_display_mode dsi_mode, cur_dsi_mode, *panel_dsi_mode;
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700283 struct drm_display_mode cur_mode;
Raviteja Tamatamd7174ec2017-11-17 18:48:56 +0530284 struct drm_crtc_state *crtc_state;
285
286 crtc_state = container_of(mode, struct drm_crtc_state, mode);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700287
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400288 if (!bridge || !mode || !adjusted_mode) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700289 pr_err("Invalid params\n");
290 return false;
291 }
292
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500293 display = c_bridge->display;
294 if (!display) {
295 pr_err("Invalid params\n");
296 return false;
297 }
298
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700299 convert_to_dsi_mode(mode, &dsi_mode);
300
Ray Zhanga8a5dfe2018-01-22 10:29:01 +0800301 /* external bridge doesn't use priv_info and dsi_mode_flags */
302 if (!dsi_display_has_ext_bridge(display)) {
303 /*
304 * retrieve dsi mode from dsi driver's cache since not safe to
305 * take the drm mode config mutex in all paths
306 */
307 rc = dsi_display_find_mode(display, &dsi_mode, &panel_dsi_mode);
308 if (rc)
309 return rc;
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500310
Ray Zhanga8a5dfe2018-01-22 10:29:01 +0800311 /*
312 * propagate the private info to the adjusted_mode derived dsi
313 * mode
314 */
315 dsi_mode.priv_info = panel_dsi_mode->priv_info;
316 dsi_mode.dsi_mode_flags = panel_dsi_mode->dsi_mode_flags;
317 }
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500318
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -0700319 rc = dsi_display_validate_mode(c_bridge->display, &dsi_mode,
320 DSI_VALIDATE_FLAG_ALLOW_ADJUST);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700321 if (rc) {
Ajay Singh Parmar35c1e9f2016-06-10 23:25:13 -0700322 pr_err("[%d] mode is not valid, rc=%d\n", c_bridge->id, rc);
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700323 return false;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700324 }
325
Raviteja Tamatamd7174ec2017-11-17 18:48:56 +0530326 if (bridge->encoder && bridge->encoder->crtc &&
327 crtc_state->crtc) {
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530328
Raviteja Tamatamd7174ec2017-11-17 18:48:56 +0530329 convert_to_dsi_mode(&crtc_state->crtc->state->mode,
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530330 &cur_dsi_mode);
331 rc = dsi_display_validate_mode_vrr(c_bridge->display,
332 &cur_dsi_mode, &dsi_mode);
333 if (rc)
334 pr_debug("[%s] vrr mode mismatch failure rc=%d\n",
335 c_bridge->display->name, rc);
336
Raviteja Tamatamd7174ec2017-11-17 18:48:56 +0530337 cur_mode = crtc_state->crtc->mode;
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700338
Jeykumar Sankaran2e122702017-12-14 16:36:15 -0800339 /* No DMS/VRR when drm pipeline is changing */
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530340 if (!drm_mode_equal(&cur_mode, adjusted_mode) &&
Jeykumar Sankaran2e122702017-12-14 16:36:15 -0800341 (!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) &&
342 (!crtc_state->active_changed ||
343 display->is_cont_splash_enabled))
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700344 dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS;
345 }
346
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500347 /* convert back to drm mode, propagating the private info & flags */
Chandan Uddaraju3f2cf422017-06-15 15:37:39 -0700348 dsi_convert_to_drm_mode(&dsi_mode, adjusted_mode);
Jeykumar Sankarana7c7bbe2017-05-31 18:12:05 -0700349
350 return true;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700351}
352
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700353int dsi_conn_get_mode_info(const struct drm_display_mode *drm_mode,
354 struct msm_mode_info *mode_info,
Alan Kwongb1bca602017-09-18 17:28:45 -0400355 u32 max_mixer_width, void *display)
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700356{
357 struct dsi_display_mode dsi_mode;
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700358 struct dsi_mode_info *timing;
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700359
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700360 if (!drm_mode || !mode_info)
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700361 return -EINVAL;
362
363 convert_to_dsi_mode(drm_mode, &dsi_mode);
364
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700365 if (!dsi_mode.priv_info)
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700366 return -EINVAL;
367
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700368 memset(mode_info, 0, sizeof(*mode_info));
369
370 timing = &dsi_mode.timing;
371 mode_info->frame_rate = dsi_mode.timing.refresh_rate;
372 mode_info->vtotal = DSI_V_TOTAL(timing);
373 mode_info->prefill_lines = dsi_mode.priv_info->panel_prefill_lines;
374 mode_info->jitter_numer = dsi_mode.priv_info->panel_jitter_numer;
375 mode_info->jitter_denom = dsi_mode.priv_info->panel_jitter_denom;
Vara Reddy812bd722017-09-20 07:19:19 -0700376 mode_info->clk_rate = dsi_mode.priv_info->clk_rate_hz;
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700377
378 memcpy(&mode_info->topology, &dsi_mode.priv_info->topology,
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700379 sizeof(struct msm_display_topology));
380
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700381 mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE;
382 if (dsi_mode.priv_info->dsc_enabled) {
383 mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_DSC;
384 memcpy(&mode_info->comp_info.dsc_info, &dsi_mode.priv_info->dsc,
385 sizeof(dsi_mode.priv_info->dsc));
386 }
387
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700388 if (dsi_mode.priv_info->roi_caps.enabled) {
389 memcpy(&mode_info->roi_caps, &dsi_mode.priv_info->roi_caps,
390 sizeof(dsi_mode.priv_info->roi_caps));
391 }
392
Jeykumar Sankaran6b345ac2017-03-15 19:17:19 -0700393 return 0;
394}
395
Ray Zhanga8a5dfe2018-01-22 10:29:01 +0800396int dsi_conn_ext_bridge_get_mode_info(const struct drm_display_mode *drm_mode,
397 struct msm_mode_info *mode_info,
398 u32 max_mixer_width, void *display)
399{
400 struct msm_display_topology *topology;
401 struct dsi_display_mode dsi_mode;
402 struct dsi_mode_info *timing;
403
404 if (!drm_mode || !mode_info)
405 return -EINVAL;
406
407 convert_to_dsi_mode(drm_mode, &dsi_mode);
408
409 memset(mode_info, 0, sizeof(*mode_info));
410
411 timing = &dsi_mode.timing;
412 mode_info->frame_rate = dsi_mode.timing.refresh_rate;
413 mode_info->vtotal = DSI_V_TOTAL(timing);
414
415 topology = &mode_info->topology;
416 topology->num_lm = (max_mixer_width <= drm_mode->hdisplay) ? 2 : 1;
417 topology->num_enc = 0;
418 topology->num_intf = topology->num_lm;
419
420 mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_NONE;
421
422 return 0;
423}
424
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700425static const struct drm_bridge_funcs dsi_bridge_ops = {
426 .attach = dsi_bridge_attach,
427 .mode_fixup = dsi_bridge_mode_fixup,
428 .pre_enable = dsi_bridge_pre_enable,
429 .enable = dsi_bridge_enable,
430 .disable = dsi_bridge_disable,
431 .post_disable = dsi_bridge_post_disable,
432 .mode_set = dsi_bridge_mode_set,
433};
434
Alan Kwong769fba92017-11-13 16:50:36 -0500435int dsi_conn_set_info_blob(struct drm_connector *connector,
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700436 void *info, void *display, struct msm_mode_info *mode_info)
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700437{
Dhaval Patel4e574842016-08-23 15:11:37 -0700438 struct dsi_display *dsi_display = display;
439 struct dsi_panel *panel;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700440
Dhaval Patel4e574842016-08-23 15:11:37 -0700441 if (!info || !dsi_display)
442 return -EINVAL;
443
Clarence Ip458a5d02017-11-27 18:15:16 -0500444 dsi_display->drm_conn = connector;
445
Dhaval Patel4e574842016-08-23 15:11:37 -0700446 sde_kms_info_add_keystr(info,
447 "display type", dsi_display->display_type);
448
449 switch (dsi_display->type) {
450 case DSI_DISPLAY_SINGLE:
451 sde_kms_info_add_keystr(info, "display config",
452 "single display");
453 break;
454 case DSI_DISPLAY_EXT_BRIDGE:
455 sde_kms_info_add_keystr(info, "display config", "ext bridge");
456 break;
457 case DSI_DISPLAY_SPLIT:
458 sde_kms_info_add_keystr(info, "display config",
459 "split display");
460 break;
461 case DSI_DISPLAY_SPLIT_EXT_BRIDGE:
462 sde_kms_info_add_keystr(info, "display config",
463 "split ext bridge");
464 break;
465 default:
466 pr_debug("invalid display type:%d\n", dsi_display->type);
467 break;
468 }
469
470 if (!dsi_display->panel) {
471 pr_debug("invalid panel data\n");
472 goto end;
473 }
474
475 panel = dsi_display->panel;
476 sde_kms_info_add_keystr(info, "panel name", panel->name);
477
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700478 switch (panel->panel_mode) {
Dhaval Patel4e574842016-08-23 15:11:37 -0700479 case DSI_OP_VIDEO_MODE:
480 sde_kms_info_add_keystr(info, "panel mode", "video");
481 break;
482 case DSI_OP_CMD_MODE:
483 sde_kms_info_add_keystr(info, "panel mode", "command");
Alan Kwong9aa061c2016-11-06 21:17:12 -0500484 sde_kms_info_add_keyint(info, "mdp_transfer_time_us",
485 panel->cmd_config.mdp_transfer_time_us);
Dhaval Patel4e574842016-08-23 15:11:37 -0700486 break;
487 default:
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700488 pr_debug("invalid panel type:%d\n", panel->panel_mode);
Dhaval Patel4e574842016-08-23 15:11:37 -0700489 break;
490 }
491 sde_kms_info_add_keystr(info, "dfps support",
492 panel->dfps_caps.dfps_support ? "true" : "false");
493
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530494 if (panel->dfps_caps.dfps_support) {
495 sde_kms_info_add_keyint(info, "min_fps",
496 panel->dfps_caps.min_refresh_rate);
497 sde_kms_info_add_keyint(info, "max_fps",
498 panel->dfps_caps.max_refresh_rate);
499 }
500
Dhaval Patel4e574842016-08-23 15:11:37 -0700501 switch (panel->phy_props.rotation) {
502 case DSI_PANEL_ROTATE_NONE:
503 sde_kms_info_add_keystr(info, "panel orientation", "none");
504 break;
505 case DSI_PANEL_ROTATE_H_FLIP:
506 sde_kms_info_add_keystr(info, "panel orientation", "horz flip");
507 break;
508 case DSI_PANEL_ROTATE_V_FLIP:
509 sde_kms_info_add_keystr(info, "panel orientation", "vert flip");
510 break;
Dhaval Patel9b349aa2017-05-17 17:06:15 -0700511 case DSI_PANEL_ROTATE_HV_FLIP:
512 sde_kms_info_add_keystr(info, "panel orientation",
513 "horz & vert flip");
514 break;
Dhaval Patel4e574842016-08-23 15:11:37 -0700515 default:
516 pr_debug("invalid panel rotation:%d\n",
517 panel->phy_props.rotation);
518 break;
519 }
520
521 switch (panel->bl_config.type) {
522 case DSI_BACKLIGHT_PWM:
523 sde_kms_info_add_keystr(info, "backlight type", "pwm");
524 break;
525 case DSI_BACKLIGHT_WLED:
526 sde_kms_info_add_keystr(info, "backlight type", "wled");
527 break;
528 case DSI_BACKLIGHT_DCS:
529 sde_kms_info_add_keystr(info, "backlight type", "dcs");
530 break;
531 default:
532 pr_debug("invalid panel backlight type:%d\n",
533 panel->bl_config.type);
534 break;
535 }
536
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700537 if (mode_info && mode_info->roi_caps.enabled) {
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400538 sde_kms_info_add_keyint(info, "partial_update_num_roi",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700539 mode_info->roi_caps.num_roi);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400540 sde_kms_info_add_keyint(info, "partial_update_xstart",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700541 mode_info->roi_caps.align.xstart_pix_align);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400542 sde_kms_info_add_keyint(info, "partial_update_walign",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700543 mode_info->roi_caps.align.width_pix_align);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400544 sde_kms_info_add_keyint(info, "partial_update_wmin",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700545 mode_info->roi_caps.align.min_width);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400546 sde_kms_info_add_keyint(info, "partial_update_ystart",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700547 mode_info->roi_caps.align.ystart_pix_align);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400548 sde_kms_info_add_keyint(info, "partial_update_halign",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700549 mode_info->roi_caps.align.height_pix_align);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400550 sde_kms_info_add_keyint(info, "partial_update_hmin",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700551 mode_info->roi_caps.align.min_height);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400552 sde_kms_info_add_keyint(info, "partial_update_roimerge",
Jeykumar Sankaran736d79d2017-10-05 17:44:24 -0700553 mode_info->roi_caps.merge_rois);
Lloyd Atkinsone53b7372017-03-22 17:16:47 -0400554 }
555
Dhaval Patel4e574842016-08-23 15:11:37 -0700556end:
Clarence Ip40d7d592016-07-15 16:02:26 -0400557 return 0;
558}
559
560enum drm_connector_status dsi_conn_detect(struct drm_connector *conn,
561 bool force,
562 void *display)
563{
564 enum drm_connector_status status = connector_status_unknown;
Clarence Ipa4039322016-07-15 16:23:59 -0400565 struct msm_display_info info;
Clarence Ip40d7d592016-07-15 16:02:26 -0400566 int rc;
567
568 if (!conn || !display)
569 return status;
570
571 /* get display dsi_info */
Clarence Ipa4039322016-07-15 16:23:59 -0400572 memset(&info, 0x0, sizeof(info));
573 rc = dsi_display_get_info(&info, display);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700574 if (rc) {
Clarence Ipa4039322016-07-15 16:23:59 -0400575 pr_err("failed to get display info, rc=%d\n", rc);
Clarence Ip40d7d592016-07-15 16:02:26 -0400576 return connector_status_disconnected;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700577 }
578
Clarence Ipa4039322016-07-15 16:23:59 -0400579 if (info.capabilities & MSM_DISPLAY_CAP_HOT_PLUG)
580 status = (info.is_connected ? connector_status_connected :
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700581 connector_status_disconnected);
Clarence Ip40d7d592016-07-15 16:02:26 -0400582 else
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700583 status = connector_status_connected;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700584
Clarence Ipa4039322016-07-15 16:23:59 -0400585 conn->display_info.width_mm = info.width_mm;
586 conn->display_info.height_mm = info.height_mm;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700587
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700588 return status;
589}
590
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700591void dsi_connector_put_modes(struct drm_connector *connector,
592 void *display)
593{
594 struct drm_display_mode *drm_mode;
595 struct dsi_display_mode dsi_mode;
596
597 if (!connector || !display)
598 return;
599
600 list_for_each_entry(drm_mode, &connector->modes, head) {
601 convert_to_dsi_mode(drm_mode, &dsi_mode);
602 dsi_display_put_mode(display, &dsi_mode);
603 }
604}
605
Clarence Ip40d7d592016-07-15 16:02:26 -0400606int dsi_connector_get_modes(struct drm_connector *connector,
607 void *display)
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700608{
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700609 u32 count = 0;
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700610 struct dsi_display_mode *modes = NULL;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700611 struct drm_display_mode drm_mode;
Clarence Ip40d7d592016-07-15 16:02:26 -0400612 int rc, i;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700613
Clarence Ip40d7d592016-07-15 16:02:26 -0400614 if (sde_connector_get_panel(connector)) {
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700615 /*
616 * TODO: If drm_panel is attached, query modes from the panel.
617 * This is complicated in split dsi cases because panel is not
618 * attached to both connectors.
619 */
620 goto end;
621 }
Jeykumar Sankaran446a5f12017-05-09 20:30:39 -0700622 rc = dsi_display_get_mode_count(display, &count);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700623 if (rc) {
Clarence Ip40d7d592016-07-15 16:02:26 -0400624 pr_err("failed to get num of modes, rc=%d\n", rc);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700625 goto end;
626 }
627
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500628 rc = dsi_display_get_modes(display, &modes);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700629 if (rc) {
Clarence Ip40d7d592016-07-15 16:02:26 -0400630 pr_err("failed to get modes, rc=%d\n", rc);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700631 count = 0;
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500632 goto end;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700633 }
634
635 for (i = 0; i < count; i++) {
636 struct drm_display_mode *m;
637
638 memset(&drm_mode, 0x0, sizeof(drm_mode));
Chandan Uddaraju3f2cf422017-06-15 15:37:39 -0700639 dsi_convert_to_drm_mode(&modes[i], &drm_mode);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700640 m = drm_mode_duplicate(connector->dev, &drm_mode);
641 if (!m) {
Clarence Ip40d7d592016-07-15 16:02:26 -0400642 pr_err("failed to add mode %ux%u\n",
643 drm_mode.hdisplay,
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700644 drm_mode.vdisplay);
645 count = -ENOMEM;
Lloyd Atkinson560785e2017-11-16 14:04:15 -0500646 goto end;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700647 }
648 m->width_mm = connector->display_info.width_mm;
649 m->height_mm = connector->display_info.height_mm;
650 drm_mode_probed_add(connector, m);
651 }
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700652end:
653 pr_debug("MODE COUNT =%d\n\n", count);
654 return count;
655}
656
Clarence Ip40d7d592016-07-15 16:02:26 -0400657enum drm_mode_status dsi_conn_mode_valid(struct drm_connector *connector,
658 struct drm_display_mode *mode,
659 void *display)
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700660{
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700661 struct dsi_display_mode dsi_mode;
Clarence Ip40d7d592016-07-15 16:02:26 -0400662 int rc;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700663
664 if (!connector || !mode) {
665 pr_err("Invalid params\n");
666 return MODE_ERROR;
667 }
668
669 convert_to_dsi_mode(mode, &dsi_mode);
670
Clarence Ip40d7d592016-07-15 16:02:26 -0400671 rc = dsi_display_validate_mode(display, &dsi_mode,
Ajay Singh Parmar62f795b2016-06-10 23:20:23 -0700672 DSI_VALIDATE_FLAG_ALLOW_ADJUST);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700673 if (rc) {
Clarence Ip40d7d592016-07-15 16:02:26 -0400674 pr_err("mode not supported, rc=%d\n", rc);
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700675 return MODE_BAD;
676 }
677
678 return MODE_OK;
679}
680
Lloyd Atkinson05d75512017-01-17 14:45:51 -0500681int dsi_conn_pre_kickoff(struct drm_connector *connector,
682 void *display,
683 struct msm_display_kickoff_params *params)
684{
685 if (!connector || !display || !params) {
686 pr_err("Invalid params\n");
687 return -EINVAL;
688 }
689
690 return dsi_display_pre_kickoff(display, params);
691}
692
Clarence Ip80ada7f2017-05-04 09:55:21 -0700693void dsi_conn_enable_event(struct drm_connector *connector,
694 uint32_t event_idx, bool enable, void *display)
695{
696 struct dsi_event_cb_info event_info;
697
698 memset(&event_info, 0, sizeof(event_info));
699
700 event_info.event_cb = sde_connector_trigger_event;
701 event_info.event_usr_ptr = connector;
702
703 dsi_display_enable_event(display, event_idx, &event_info, enable);
704}
705
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530706int dsi_conn_post_kickoff(struct drm_connector *connector)
707{
708 struct drm_encoder *encoder;
709 struct dsi_bridge *c_bridge;
710 struct dsi_display_mode adj_mode;
711 struct dsi_display *display;
712 struct dsi_display_ctrl *m_ctrl, *ctrl;
713 int i, rc = 0;
714
Dhaval Patelbadfefd2017-09-26 13:58:02 -0700715 if (!connector || !connector->state) {
716 pr_err("invalid connector or connector state");
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530717 return -EINVAL;
Dhaval Patelbadfefd2017-09-26 13:58:02 -0700718 }
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530719
720 encoder = connector->state->best_encoder;
Dhaval Patelbadfefd2017-09-26 13:58:02 -0700721 if (!encoder) {
722 pr_debug("best encoder is not available");
723 return 0;
724 }
725
Raviteja Tamatam68892de2017-06-20 04:47:19 +0530726 c_bridge = to_dsi_bridge(encoder->bridge);
727 adj_mode = c_bridge->dsi_mode;
728 display = c_bridge->display;
729
730 if (adj_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR) {
731 m_ctrl = &display->ctrl[display->clk_master_idx];
732 rc = dsi_ctrl_timing_db_update(m_ctrl->ctrl, false);
733 if (rc) {
734 pr_err("[%s] failed to dfps update rc=%d\n",
735 display->name, rc);
736 return -EINVAL;
737 }
738
739 /* Update the rest of the controllers */
740 for (i = 0; i < display->ctrl_count; i++) {
741 ctrl = &display->ctrl[i];
742 if (!ctrl->ctrl || (ctrl == m_ctrl))
743 continue;
744
745 rc = dsi_ctrl_timing_db_update(ctrl->ctrl, false);
746 if (rc) {
747 pr_err("[%s] failed to dfps update rc=%d\n",
748 display->name, rc);
749 return -EINVAL;
750 }
751 }
752
753 c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_VRR;
754 }
755
756 return 0;
757}
758
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700759struct dsi_bridge *dsi_drm_bridge_init(struct dsi_display *display,
760 struct drm_device *dev,
761 struct drm_encoder *encoder)
762{
763 int rc = 0;
764 struct dsi_bridge *bridge;
765
766 bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
767 if (!bridge) {
768 rc = -ENOMEM;
769 goto error;
770 }
771
772 bridge->display = display;
773 bridge->base.funcs = &dsi_bridge_ops;
774 bridge->base.encoder = encoder;
775
776 rc = drm_bridge_attach(dev, &bridge->base);
777 if (rc) {
778 pr_err("failed to attach bridge, rc=%d\n", rc);
779 goto error_free_bridge;
780 }
781
782 encoder->bridge = &bridge->base;
783 return bridge;
784error_free_bridge:
785 kfree(bridge);
786error:
787 return ERR_PTR(rc);
788}
789
790void dsi_drm_bridge_cleanup(struct dsi_bridge *bridge)
791{
Lloyd Atkinsone404caf2016-07-13 17:26:45 -0400792 if (bridge && bridge->base.encoder)
793 bridge->base.encoder->bridge = NULL;
Ajay Singh Parmar6bccfec2016-05-16 17:56:30 -0700794
795 kfree(bridge);
796}