diff options
author | Vinay Simha BN <simhavcs@gmail.com> | 2016-08-25 22:37:07 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2016-09-16 11:32:48 -0400 |
commit | c96f566273bf086fe18a294ac37edf2d451ff024 (patch) | |
tree | 8f05606c5ac1444fad24d26edf82e43bd886e06b /drivers/gpu/drm/panel | |
parent | cc4b13dd6df1cd80c4c8cddc4908532893a26af5 (diff) |
drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel
Add support for the JDI LT070ME05000 WUXGA DSI panel used in
Nexus 7 2013 devices.
Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
https://android.googlesource.com/kernel/msm.git
And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
git://codeaurora.org/kernel/msm-3.10.git LNX.LA.3.6_rb1.27
Cc: Archit Taneja <archit.taneja@gmail.com>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Emil Velikov <emil.l.velikov@gmail.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
Tested-by: John Stultz <john.stultz@linaro.org>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 532 |
3 files changed, 544 insertions, 0 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 1500ab99f548..62aba976e744 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig | |||
@@ -18,6 +18,17 @@ config DRM_PANEL_SIMPLE | |||
18 | that it can be automatically turned off when the panel goes into a | 18 | that it can be automatically turned off when the panel goes into a |
19 | low power state. | 19 | low power state. |
20 | 20 | ||
21 | config DRM_PANEL_JDI_LT070ME05000 | ||
22 | tristate "JDI LT070ME05000 WUXGA DSI panel" | ||
23 | depends on OF | ||
24 | depends on DRM_MIPI_DSI | ||
25 | depends on BACKLIGHT_CLASS_DEVICE | ||
26 | help | ||
27 | Say Y here if you want to enable support for JDI DSI video mode | ||
28 | panel as found in Google Nexus 7 (2013) devices. | ||
29 | The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses | ||
30 | 24 bit per pixel. | ||
31 | |||
21 | config DRM_PANEL_SAMSUNG_LD9040 | 32 | config DRM_PANEL_SAMSUNG_LD9040 |
22 | tristate "Samsung LD9040 RGB/SPI panel" | 33 | tristate "Samsung LD9040 RGB/SPI panel" |
23 | depends on OF && SPI | 34 | depends on OF && SPI |
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index f277eed933d6..a5c7ec0236e0 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o | 1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
2 | obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o | ||
2 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o | 3 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o |
3 | obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o | 4 | obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o |
4 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o | 5 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o |
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c new file mode 100644 index 000000000000..5b2340ef74ed --- /dev/null +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | |||
@@ -0,0 +1,532 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 InforceComputing | ||
3 | * Author: Vinay Simha BN <simhavcs@gmail.com> | ||
4 | * | ||
5 | * Copyright (C) 2016 Linaro Ltd | ||
6 | * Author: Sumit Semwal <sumit.semwal@linaro.org> | ||
7 | * | ||
8 | * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a | ||
9 | * JDI model LT070ME05000, and its data sheet is at: | ||
10 | * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License version 2 as published by | ||
14 | * the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
19 | * more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along with | ||
22 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
23 | */ | ||
24 | #include <linux/backlight.h> | ||
25 | #include <linux/gpio/consumer.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/of.h> | ||
28 | #include <linux/regulator/consumer.h> | ||
29 | |||
30 | #include <drm/drmP.h> | ||
31 | #include <drm/drm_crtc.h> | ||
32 | #include <drm/drm_mipi_dsi.h> | ||
33 | #include <drm/drm_panel.h> | ||
34 | |||
35 | #include <video/mipi_display.h> | ||
36 | |||
37 | static const char * const regulator_names[] = { | ||
38 | "vddp", | ||
39 | "iovcc" | ||
40 | }; | ||
41 | |||
42 | struct jdi_panel { | ||
43 | struct drm_panel base; | ||
44 | struct mipi_dsi_device *dsi; | ||
45 | |||
46 | struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; | ||
47 | |||
48 | struct gpio_desc *enable_gpio; | ||
49 | struct gpio_desc *reset_gpio; | ||
50 | struct gpio_desc *dcdc_en_gpio; | ||
51 | struct backlight_device *backlight; | ||
52 | |||
53 | bool prepared; | ||
54 | bool enabled; | ||
55 | |||
56 | const struct drm_display_mode *mode; | ||
57 | }; | ||
58 | |||
59 | static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel) | ||
60 | { | ||
61 | return container_of(panel, struct jdi_panel, base); | ||
62 | } | ||
63 | |||
64 | static int jdi_panel_init(struct jdi_panel *jdi) | ||
65 | { | ||
66 | struct mipi_dsi_device *dsi = jdi->dsi; | ||
67 | struct device *dev = &jdi->dsi->dev; | ||
68 | int ret; | ||
69 | |||
70 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; | ||
71 | |||
72 | ret = mipi_dsi_dcs_soft_reset(dsi); | ||
73 | if (ret < 0) | ||
74 | return ret; | ||
75 | |||
76 | usleep_range(10000, 20000); | ||
77 | |||
78 | ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4); | ||
79 | if (ret < 0) { | ||
80 | dev_err(dev, "failed to set pixel format: %d\n", ret); | ||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1); | ||
85 | if (ret < 0) { | ||
86 | dev_err(dev, "failed to set column address: %d\n", ret); | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1); | ||
91 | if (ret < 0) { | ||
92 | dev_err(dev, "failed to set page address: %d\n", ret); | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers | ||
98 | * are active | ||
99 | * BIT(3) BL = 1 Backlight Control On | ||
100 | * BIT(2) DD = 0 Display Dimming is Off | ||
101 | */ | ||
102 | ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, | ||
103 | (u8[]){ 0x24 }, 1); | ||
104 | if (ret < 0) { | ||
105 | dev_err(dev, "failed to write control display: %d\n", ret); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | /* CABC off */ | ||
110 | ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE, | ||
111 | (u8[]){ 0x00 }, 1); | ||
112 | if (ret < 0) { | ||
113 | dev_err(dev, "failed to set cabc off: %d\n", ret); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | ret = mipi_dsi_dcs_exit_sleep_mode(dsi); | ||
118 | if (ret < 0) { | ||
119 | dev_err(dev, "failed to set exit sleep mode: %d\n", ret); | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | msleep(120); | ||
124 | |||
125 | ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2); | ||
126 | if (ret < 0) { | ||
127 | dev_err(dev, "failed to set mcap: %d\n", ret); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | mdelay(10); | ||
132 | |||
133 | /* Interface setting, video mode */ | ||
134 | ret = mipi_dsi_generic_write(dsi, (u8[]) | ||
135 | {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6); | ||
136 | if (ret < 0) { | ||
137 | dev_err(dev, "failed to set display interface setting: %d\n" | ||
138 | , ret); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | mdelay(20); | ||
143 | |||
144 | ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2); | ||
145 | if (ret < 0) { | ||
146 | dev_err(dev, "failed to set default values for mcap: %d\n" | ||
147 | , ret); | ||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int jdi_panel_on(struct jdi_panel *jdi) | ||
155 | { | ||
156 | struct mipi_dsi_device *dsi = jdi->dsi; | ||
157 | struct device *dev = &jdi->dsi->dev; | ||
158 | int ret; | ||
159 | |||
160 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; | ||
161 | |||
162 | ret = mipi_dsi_dcs_set_display_on(dsi); | ||
163 | if (ret < 0) | ||
164 | dev_err(dev, "failed to set display on: %d\n", ret); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static void jdi_panel_off(struct jdi_panel *jdi) | ||
170 | { | ||
171 | struct mipi_dsi_device *dsi = jdi->dsi; | ||
172 | struct device *dev = &jdi->dsi->dev; | ||
173 | int ret; | ||
174 | |||
175 | dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; | ||
176 | |||
177 | ret = mipi_dsi_dcs_set_display_off(dsi); | ||
178 | if (ret < 0) | ||
179 | dev_err(dev, "failed to set display off: %d\n", ret); | ||
180 | |||
181 | ret = mipi_dsi_dcs_enter_sleep_mode(dsi); | ||
182 | if (ret < 0) | ||
183 | dev_err(dev, "failed to enter sleep mode: %d\n", ret); | ||
184 | |||
185 | msleep(100); | ||
186 | } | ||
187 | |||
188 | static int jdi_panel_disable(struct drm_panel *panel) | ||
189 | { | ||
190 | struct jdi_panel *jdi = to_jdi_panel(panel); | ||
191 | |||
192 | if (!jdi->enabled) | ||
193 | return 0; | ||
194 | |||
195 | jdi->backlight->props.power = FB_BLANK_POWERDOWN; | ||
196 | backlight_update_status(jdi->backlight); | ||
197 | |||
198 | jdi->enabled = false; | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int jdi_panel_unprepare(struct drm_panel *panel) | ||
204 | { | ||
205 | struct jdi_panel *jdi = to_jdi_panel(panel); | ||
206 | struct device *dev = &jdi->dsi->dev; | ||
207 | int ret; | ||
208 | |||
209 | if (!jdi->prepared) | ||
210 | return 0; | ||
211 | |||
212 | jdi_panel_off(jdi); | ||
213 | |||
214 | ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); | ||
215 | if (ret < 0) | ||
216 | dev_err(dev, "regulator disable failed, %d\n", ret); | ||
217 | |||
218 | gpiod_set_value(jdi->enable_gpio, 0); | ||
219 | |||
220 | gpiod_set_value(jdi->reset_gpio, 1); | ||
221 | |||
222 | gpiod_set_value(jdi->dcdc_en_gpio, 0); | ||
223 | |||
224 | jdi->prepared = false; | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int jdi_panel_prepare(struct drm_panel *panel) | ||
230 | { | ||
231 | struct jdi_panel *jdi = to_jdi_panel(panel); | ||
232 | struct device *dev = &jdi->dsi->dev; | ||
233 | int ret; | ||
234 | |||
235 | if (jdi->prepared) | ||
236 | return 0; | ||
237 | |||
238 | ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies); | ||
239 | if (ret < 0) { | ||
240 | dev_err(dev, "regulator enable failed, %d\n", ret); | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | msleep(20); | ||
245 | |||
246 | gpiod_set_value(jdi->dcdc_en_gpio, 1); | ||
247 | usleep_range(10, 20); | ||
248 | |||
249 | gpiod_set_value(jdi->reset_gpio, 0); | ||
250 | usleep_range(10, 20); | ||
251 | |||
252 | gpiod_set_value(jdi->enable_gpio, 1); | ||
253 | usleep_range(10, 20); | ||
254 | |||
255 | ret = jdi_panel_init(jdi); | ||
256 | if (ret < 0) { | ||
257 | dev_err(dev, "failed to init panel: %d\n", ret); | ||
258 | goto poweroff; | ||
259 | } | ||
260 | |||
261 | ret = jdi_panel_on(jdi); | ||
262 | if (ret < 0) { | ||
263 | dev_err(dev, "failed to set panel on: %d\n", ret); | ||
264 | goto poweroff; | ||
265 | } | ||
266 | |||
267 | jdi->prepared = true; | ||
268 | |||
269 | return 0; | ||
270 | |||
271 | poweroff: | ||
272 | ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); | ||
273 | if (ret < 0) | ||
274 | dev_err(dev, "regulator disable failed, %d\n", ret); | ||
275 | |||
276 | gpiod_set_value(jdi->enable_gpio, 0); | ||
277 | |||
278 | gpiod_set_value(jdi->reset_gpio, 1); | ||
279 | |||
280 | gpiod_set_value(jdi->dcdc_en_gpio, 0); | ||
281 | |||
282 | return ret; | ||
283 | } | ||
284 | |||
285 | static int jdi_panel_enable(struct drm_panel *panel) | ||
286 | { | ||
287 | struct jdi_panel *jdi = to_jdi_panel(panel); | ||
288 | |||
289 | if (jdi->enabled) | ||
290 | return 0; | ||
291 | |||
292 | jdi->backlight->props.power = FB_BLANK_UNBLANK; | ||
293 | backlight_update_status(jdi->backlight); | ||
294 | |||
295 | jdi->enabled = true; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static const struct drm_display_mode default_mode = { | ||
301 | .clock = 155493, | ||
302 | .hdisplay = 1200, | ||
303 | .hsync_start = 1200 + 48, | ||
304 | .hsync_end = 1200 + 48 + 32, | ||
305 | .htotal = 1200 + 48 + 32 + 60, | ||
306 | .vdisplay = 1920, | ||
307 | .vsync_start = 1920 + 3, | ||
308 | .vsync_end = 1920 + 3 + 5, | ||
309 | .vtotal = 1920 + 3 + 5 + 6, | ||
310 | .vrefresh = 60, | ||
311 | .flags = 0, | ||
312 | }; | ||
313 | |||
314 | static int jdi_panel_get_modes(struct drm_panel *panel) | ||
315 | { | ||
316 | struct drm_display_mode *mode; | ||
317 | struct jdi_panel *jdi = to_jdi_panel(panel); | ||
318 | struct device *dev = &jdi->dsi->dev; | ||
319 | |||
320 | mode = drm_mode_duplicate(panel->drm, &default_mode); | ||
321 | if (!mode) { | ||
322 | dev_err(dev, "failed to add mode %ux%ux@%u\n", | ||
323 | default_mode.hdisplay, default_mode.vdisplay, | ||
324 | default_mode.vrefresh); | ||
325 | return -ENOMEM; | ||
326 | } | ||
327 | |||
328 | drm_mode_set_name(mode); | ||
329 | |||
330 | drm_mode_probed_add(panel->connector, mode); | ||
331 | |||
332 | panel->connector->display_info.width_mm = 95; | ||
333 | panel->connector->display_info.height_mm = 151; | ||
334 | |||
335 | return 1; | ||
336 | } | ||
337 | |||
338 | static int dsi_dcs_bl_get_brightness(struct backlight_device *bl) | ||
339 | { | ||
340 | struct mipi_dsi_device *dsi = bl_get_data(bl); | ||
341 | int ret; | ||
342 | u16 brightness = bl->props.brightness; | ||
343 | |||
344 | dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; | ||
345 | |||
346 | ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); | ||
347 | if (ret < 0) | ||
348 | return ret; | ||
349 | |||
350 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; | ||
351 | |||
352 | return brightness & 0xff; | ||
353 | } | ||
354 | |||
355 | static int dsi_dcs_bl_update_status(struct backlight_device *bl) | ||
356 | { | ||
357 | struct mipi_dsi_device *dsi = bl_get_data(bl); | ||
358 | int ret; | ||
359 | |||
360 | dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; | ||
361 | |||
362 | ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); | ||
363 | if (ret < 0) | ||
364 | return ret; | ||
365 | |||
366 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static const struct backlight_ops dsi_bl_ops = { | ||
372 | .update_status = dsi_dcs_bl_update_status, | ||
373 | .get_brightness = dsi_dcs_bl_get_brightness, | ||
374 | }; | ||
375 | |||
376 | static struct backlight_device * | ||
377 | drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi) | ||
378 | { | ||
379 | struct device *dev = &dsi->dev; | ||
380 | struct backlight_properties props; | ||
381 | |||
382 | memset(&props, 0, sizeof(props)); | ||
383 | props.type = BACKLIGHT_RAW; | ||
384 | props.brightness = 255; | ||
385 | props.max_brightness = 255; | ||
386 | |||
387 | return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, | ||
388 | &dsi_bl_ops, &props); | ||
389 | } | ||
390 | |||
391 | static const struct drm_panel_funcs jdi_panel_funcs = { | ||
392 | .disable = jdi_panel_disable, | ||
393 | .unprepare = jdi_panel_unprepare, | ||
394 | .prepare = jdi_panel_prepare, | ||
395 | .enable = jdi_panel_enable, | ||
396 | .get_modes = jdi_panel_get_modes, | ||
397 | }; | ||
398 | |||
399 | static const struct of_device_id jdi_of_match[] = { | ||
400 | { .compatible = "jdi,lt070me05000", }, | ||
401 | { } | ||
402 | }; | ||
403 | MODULE_DEVICE_TABLE(of, jdi_of_match); | ||
404 | |||
405 | static int jdi_panel_add(struct jdi_panel *jdi) | ||
406 | { | ||
407 | struct device *dev = &jdi->dsi->dev; | ||
408 | int ret; | ||
409 | unsigned int i; | ||
410 | |||
411 | jdi->mode = &default_mode; | ||
412 | |||
413 | for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++) | ||
414 | jdi->supplies[i].supply = regulator_names[i]; | ||
415 | |||
416 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies), | ||
417 | jdi->supplies); | ||
418 | if (ret < 0) { | ||
419 | dev_err(dev, "failed to init regulator, ret=%d\n", ret); | ||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); | ||
424 | if (IS_ERR(jdi->enable_gpio)) { | ||
425 | ret = PTR_ERR(jdi->enable_gpio); | ||
426 | dev_err(dev, "cannot get enable-gpio %d\n", ret); | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); | ||
431 | if (IS_ERR(jdi->reset_gpio)) { | ||
432 | ret = PTR_ERR(jdi->reset_gpio); | ||
433 | dev_err(dev, "cannot get reset-gpios %d\n", ret); | ||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW); | ||
438 | if (IS_ERR(jdi->dcdc_en_gpio)) { | ||
439 | ret = PTR_ERR(jdi->dcdc_en_gpio); | ||
440 | dev_err(dev, "cannot get dcdc-en-gpio %d\n", ret); | ||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi); | ||
445 | if (IS_ERR(jdi->backlight)) { | ||
446 | ret = PTR_ERR(jdi->backlight); | ||
447 | dev_err(dev, "failed to register backlight %d\n", ret); | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | drm_panel_init(&jdi->base); | ||
452 | jdi->base.funcs = &jdi_panel_funcs; | ||
453 | jdi->base.dev = &jdi->dsi->dev; | ||
454 | |||
455 | ret = drm_panel_add(&jdi->base); | ||
456 | |||
457 | return ret; | ||
458 | } | ||
459 | |||
460 | static void jdi_panel_del(struct jdi_panel *jdi) | ||
461 | { | ||
462 | if (jdi->base.dev) | ||
463 | drm_panel_remove(&jdi->base); | ||
464 | } | ||
465 | |||
466 | static int jdi_panel_probe(struct mipi_dsi_device *dsi) | ||
467 | { | ||
468 | struct jdi_panel *jdi; | ||
469 | int ret; | ||
470 | |||
471 | dsi->lanes = 4; | ||
472 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
473 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | | ||
474 | MIPI_DSI_CLOCK_NON_CONTINUOUS; | ||
475 | |||
476 | jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL); | ||
477 | if (!jdi) | ||
478 | return -ENOMEM; | ||
479 | |||
480 | mipi_dsi_set_drvdata(dsi, jdi); | ||
481 | |||
482 | jdi->dsi = dsi; | ||
483 | |||
484 | ret = jdi_panel_add(jdi); | ||
485 | if (ret < 0) | ||
486 | return ret; | ||
487 | |||
488 | return mipi_dsi_attach(dsi); | ||
489 | } | ||
490 | |||
491 | static int jdi_panel_remove(struct mipi_dsi_device *dsi) | ||
492 | { | ||
493 | struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); | ||
494 | int ret; | ||
495 | |||
496 | ret = jdi_panel_disable(&jdi->base); | ||
497 | if (ret < 0) | ||
498 | dev_err(&dsi->dev, "failed to disable panel: %d\n", ret); | ||
499 | |||
500 | ret = mipi_dsi_detach(dsi); | ||
501 | if (ret < 0) | ||
502 | dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", | ||
503 | ret); | ||
504 | |||
505 | drm_panel_detach(&jdi->base); | ||
506 | jdi_panel_del(jdi); | ||
507 | |||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | static void jdi_panel_shutdown(struct mipi_dsi_device *dsi) | ||
512 | { | ||
513 | struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); | ||
514 | |||
515 | jdi_panel_disable(&jdi->base); | ||
516 | } | ||
517 | |||
518 | static struct mipi_dsi_driver jdi_panel_driver = { | ||
519 | .driver = { | ||
520 | .name = "panel-jdi-lt070me05000", | ||
521 | .of_match_table = jdi_of_match, | ||
522 | }, | ||
523 | .probe = jdi_panel_probe, | ||
524 | .remove = jdi_panel_remove, | ||
525 | .shutdown = jdi_panel_shutdown, | ||
526 | }; | ||
527 | module_mipi_dsi_driver(jdi_panel_driver); | ||
528 | |||
529 | MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>"); | ||
530 | MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>"); | ||
531 | MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA"); | ||
532 | MODULE_LICENSE("GPL v2"); | ||