diff options
author | Werner Johansson <werner.johansson@sonymobile.com> | 2015-10-30 20:38:28 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2015-11-23 06:33:21 -0500 |
commit | 086ceb6b42caca65d726818b56b1e73c63df5b9e (patch) | |
tree | cd2e6273989583d6a1ca0827dd581fb62e5924d0 /drivers/gpu/drm/panel | |
parent | f74807a9f4565b22e54e8c1ae82b929018eb08ad (diff) |
drm/panel: Add Panasonic VVX10F034N00 MIPI DSI panel
This adds support for the Panasonic panel found in some Xperia Z2
tablets.
Signed-off-by: Werner Johansson <werner.johansson@sonymobile.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c | 334 |
3 files changed, 345 insertions, 0 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7d4704b1292b..15a396014a59 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig | |||
@@ -31,6 +31,16 @@ config DRM_PANEL_LG_LG4573 | |||
31 | Say Y here if you want to enable support for LG4573 RGB panel. | 31 | Say Y here if you want to enable support for LG4573 RGB panel. |
32 | To compile this driver as a module, choose M here. | 32 | To compile this driver as a module, choose M here. |
33 | 33 | ||
34 | config DRM_PANEL_PANASONIC_VVX10F034N00 | ||
35 | tristate "Panasonic VVX10F034N00 1920x1200 video mode panel" | ||
36 | depends on OF | ||
37 | depends on DRM_MIPI_DSI | ||
38 | depends on BACKLIGHT_CLASS_DEVICE | ||
39 | help | ||
40 | Say Y here if you want to enable support for Panasonic VVX10F034N00 | ||
41 | WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some | ||
42 | Xperia Z2 tablets | ||
43 | |||
34 | config DRM_PANEL_SAMSUNG_S6E8AA0 | 44 | config DRM_PANEL_SAMSUNG_S6E8AA0 |
35 | tristate "Samsung S6E8AA0 DSI video mode panel" | 45 | tristate "Samsung S6E8AA0 DSI video mode panel" |
36 | depends on OF | 46 | depends on OF |
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index d0f016dd7ddb..053287a72825 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o | 1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
2 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o | 2 | obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o |
3 | obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o | ||
3 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o | 4 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o |
4 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o | 5 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o |
5 | obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o | 6 | obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o |
diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c new file mode 100644 index 000000000000..7f915f706fa6 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c | |||
@@ -0,0 +1,334 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Red Hat | ||
3 | * Copyright (C) 2015 Sony Mobile Communications Inc. | ||
4 | * Author: Werner Johansson <werner.johansson@sonymobile.com> | ||
5 | * | ||
6 | * Based on AUO panel driver by Rob Clark <robdclark@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License version 2 as published by | ||
10 | * the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along with | ||
18 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <linux/backlight.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/regulator/consumer.h> | ||
25 | |||
26 | #include <drm/drmP.h> | ||
27 | #include <drm/drm_crtc.h> | ||
28 | #include <drm/drm_mipi_dsi.h> | ||
29 | #include <drm/drm_panel.h> | ||
30 | |||
31 | #include <video/mipi_display.h> | ||
32 | |||
33 | /* | ||
34 | * When power is turned off to this panel a minimum off time of 500ms has to be | ||
35 | * observed before powering back on as there's no external reset pin. Keep | ||
36 | * track of earliest wakeup time and delay subsequent prepare call accordingly | ||
37 | */ | ||
38 | #define MIN_POFF_MS (500) | ||
39 | |||
40 | struct wuxga_nt_panel { | ||
41 | struct drm_panel base; | ||
42 | struct mipi_dsi_device *dsi; | ||
43 | |||
44 | struct backlight_device *backlight; | ||
45 | struct regulator *supply; | ||
46 | |||
47 | bool prepared; | ||
48 | bool enabled; | ||
49 | |||
50 | ktime_t earliest_wake; | ||
51 | |||
52 | const struct drm_display_mode *mode; | ||
53 | }; | ||
54 | |||
55 | static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel) | ||
56 | { | ||
57 | return container_of(panel, struct wuxga_nt_panel, base); | ||
58 | } | ||
59 | |||
60 | static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt) | ||
61 | { | ||
62 | struct mipi_dsi_device *dsi = wuxga_nt->dsi; | ||
63 | int ret; | ||
64 | |||
65 | ret = mipi_dsi_turn_on_peripheral(dsi); | ||
66 | if (ret < 0) | ||
67 | return ret; | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int wuxga_nt_panel_disable(struct drm_panel *panel) | ||
73 | { | ||
74 | struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); | ||
75 | |||
76 | if (!wuxga_nt->enabled) | ||
77 | return 0; | ||
78 | |||
79 | mipi_dsi_shutdown_peripheral(wuxga_nt->dsi); | ||
80 | |||
81 | if (wuxga_nt->backlight) { | ||
82 | wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN; | ||
83 | wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK; | ||
84 | backlight_update_status(wuxga_nt->backlight); | ||
85 | } | ||
86 | |||
87 | wuxga_nt->enabled = false; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int wuxga_nt_panel_unprepare(struct drm_panel *panel) | ||
93 | { | ||
94 | struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); | ||
95 | |||
96 | if (!wuxga_nt->prepared) | ||
97 | return 0; | ||
98 | |||
99 | regulator_disable(wuxga_nt->supply); | ||
100 | wuxga_nt->earliest_wake = ktime_add_ms(ktime_get_real(), MIN_POFF_MS); | ||
101 | wuxga_nt->prepared = false; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int wuxga_nt_panel_prepare(struct drm_panel *panel) | ||
107 | { | ||
108 | struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); | ||
109 | int ret; | ||
110 | s64 enablewait; | ||
111 | |||
112 | if (wuxga_nt->prepared) | ||
113 | return 0; | ||
114 | |||
115 | /* | ||
116 | * If the user re-enabled the panel before the required off-time then | ||
117 | * we need to wait the remaining period before re-enabling regulator | ||
118 | */ | ||
119 | enablewait = ktime_ms_delta(wuxga_nt->earliest_wake, ktime_get_real()); | ||
120 | |||
121 | /* Sanity check, this should never happen */ | ||
122 | if (enablewait > MIN_POFF_MS) | ||
123 | enablewait = MIN_POFF_MS; | ||
124 | |||
125 | if (enablewait > 0) | ||
126 | msleep(enablewait); | ||
127 | |||
128 | ret = regulator_enable(wuxga_nt->supply); | ||
129 | if (ret < 0) | ||
130 | return ret; | ||
131 | |||
132 | /* | ||
133 | * A minimum delay of 250ms is required after power-up until commands | ||
134 | * can be sent | ||
135 | */ | ||
136 | msleep(250); | ||
137 | |||
138 | ret = wuxga_nt_panel_on(wuxga_nt); | ||
139 | if (ret < 0) { | ||
140 | dev_err(panel->dev, "failed to set panel on: %d\n", ret); | ||
141 | goto poweroff; | ||
142 | } | ||
143 | |||
144 | wuxga_nt->prepared = true; | ||
145 | |||
146 | return 0; | ||
147 | |||
148 | poweroff: | ||
149 | regulator_disable(wuxga_nt->supply); | ||
150 | |||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | static int wuxga_nt_panel_enable(struct drm_panel *panel) | ||
155 | { | ||
156 | struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel); | ||
157 | |||
158 | if (wuxga_nt->enabled) | ||
159 | return 0; | ||
160 | |||
161 | if (wuxga_nt->backlight) { | ||
162 | wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK; | ||
163 | wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK; | ||
164 | backlight_update_status(wuxga_nt->backlight); | ||
165 | } | ||
166 | |||
167 | wuxga_nt->enabled = true; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static const struct drm_display_mode default_mode = { | ||
173 | .clock = 164402, | ||
174 | .hdisplay = 1920, | ||
175 | .hsync_start = 1920 + 152, | ||
176 | .hsync_end = 1920 + 152 + 52, | ||
177 | .htotal = 1920 + 152 + 52 + 20, | ||
178 | .vdisplay = 1200, | ||
179 | .vsync_start = 1200 + 24, | ||
180 | .vsync_end = 1200 + 24 + 6, | ||
181 | .vtotal = 1200 + 24 + 6 + 48, | ||
182 | .vrefresh = 60, | ||
183 | }; | ||
184 | |||
185 | static int wuxga_nt_panel_get_modes(struct drm_panel *panel) | ||
186 | { | ||
187 | struct drm_display_mode *mode; | ||
188 | |||
189 | mode = drm_mode_duplicate(panel->drm, &default_mode); | ||
190 | if (!mode) { | ||
191 | dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n", | ||
192 | default_mode.hdisplay, default_mode.vdisplay, | ||
193 | default_mode.vrefresh); | ||
194 | return -ENOMEM; | ||
195 | } | ||
196 | |||
197 | drm_mode_set_name(mode); | ||
198 | |||
199 | drm_mode_probed_add(panel->connector, mode); | ||
200 | |||
201 | panel->connector->display_info.width_mm = 217; | ||
202 | panel->connector->display_info.height_mm = 136; | ||
203 | |||
204 | return 1; | ||
205 | } | ||
206 | |||
207 | static const struct drm_panel_funcs wuxga_nt_panel_funcs = { | ||
208 | .disable = wuxga_nt_panel_disable, | ||
209 | .unprepare = wuxga_nt_panel_unprepare, | ||
210 | .prepare = wuxga_nt_panel_prepare, | ||
211 | .enable = wuxga_nt_panel_enable, | ||
212 | .get_modes = wuxga_nt_panel_get_modes, | ||
213 | }; | ||
214 | |||
215 | static const struct of_device_id wuxga_nt_of_match[] = { | ||
216 | { .compatible = "panasonic,vvx10f034n00", }, | ||
217 | { } | ||
218 | }; | ||
219 | MODULE_DEVICE_TABLE(of, wuxga_nt_of_match); | ||
220 | |||
221 | static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt) | ||
222 | { | ||
223 | struct device *dev = &wuxga_nt->dsi->dev; | ||
224 | struct device_node *np; | ||
225 | int ret; | ||
226 | |||
227 | wuxga_nt->mode = &default_mode; | ||
228 | |||
229 | wuxga_nt->supply = devm_regulator_get(dev, "power"); | ||
230 | if (IS_ERR(wuxga_nt->supply)) | ||
231 | return PTR_ERR(wuxga_nt->supply); | ||
232 | |||
233 | np = of_parse_phandle(dev->of_node, "backlight", 0); | ||
234 | if (np) { | ||
235 | wuxga_nt->backlight = of_find_backlight_by_node(np); | ||
236 | of_node_put(np); | ||
237 | |||
238 | if (!wuxga_nt->backlight) | ||
239 | return -EPROBE_DEFER; | ||
240 | } | ||
241 | |||
242 | drm_panel_init(&wuxga_nt->base); | ||
243 | wuxga_nt->base.funcs = &wuxga_nt_panel_funcs; | ||
244 | wuxga_nt->base.dev = &wuxga_nt->dsi->dev; | ||
245 | |||
246 | ret = drm_panel_add(&wuxga_nt->base); | ||
247 | if (ret < 0) | ||
248 | goto put_backlight; | ||
249 | |||
250 | return 0; | ||
251 | |||
252 | put_backlight: | ||
253 | if (wuxga_nt->backlight) | ||
254 | put_device(&wuxga_nt->backlight->dev); | ||
255 | |||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt) | ||
260 | { | ||
261 | if (wuxga_nt->base.dev) | ||
262 | drm_panel_remove(&wuxga_nt->base); | ||
263 | |||
264 | if (wuxga_nt->backlight) | ||
265 | put_device(&wuxga_nt->backlight->dev); | ||
266 | } | ||
267 | |||
268 | static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi) | ||
269 | { | ||
270 | struct wuxga_nt_panel *wuxga_nt; | ||
271 | int ret; | ||
272 | |||
273 | dsi->lanes = 4; | ||
274 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
275 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | | ||
276 | MIPI_DSI_MODE_VIDEO_HSE | | ||
277 | MIPI_DSI_CLOCK_NON_CONTINUOUS | | ||
278 | MIPI_DSI_MODE_LPM; | ||
279 | |||
280 | wuxga_nt = devm_kzalloc(&dsi->dev, sizeof(*wuxga_nt), GFP_KERNEL); | ||
281 | if (!wuxga_nt) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | mipi_dsi_set_drvdata(dsi, wuxga_nt); | ||
285 | |||
286 | wuxga_nt->dsi = dsi; | ||
287 | |||
288 | ret = wuxga_nt_panel_add(wuxga_nt); | ||
289 | if (ret < 0) | ||
290 | return ret; | ||
291 | |||
292 | return mipi_dsi_attach(dsi); | ||
293 | } | ||
294 | |||
295 | static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) | ||
296 | { | ||
297 | struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); | ||
298 | int ret; | ||
299 | |||
300 | ret = wuxga_nt_panel_disable(&wuxga_nt->base); | ||
301 | if (ret < 0) | ||
302 | dev_err(&dsi->dev, "failed to disable panel: %d\n", ret); | ||
303 | |||
304 | ret = mipi_dsi_detach(dsi); | ||
305 | if (ret < 0) | ||
306 | dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); | ||
307 | |||
308 | drm_panel_detach(&wuxga_nt->base); | ||
309 | wuxga_nt_panel_del(wuxga_nt); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi) | ||
315 | { | ||
316 | struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); | ||
317 | |||
318 | wuxga_nt_panel_disable(&wuxga_nt->base); | ||
319 | } | ||
320 | |||
321 | static struct mipi_dsi_driver wuxga_nt_panel_driver = { | ||
322 | .driver = { | ||
323 | .name = "panel-panasonic-vvx10f034n00", | ||
324 | .of_match_table = wuxga_nt_of_match, | ||
325 | }, | ||
326 | .probe = wuxga_nt_panel_probe, | ||
327 | .remove = wuxga_nt_panel_remove, | ||
328 | .shutdown = wuxga_nt_panel_shutdown, | ||
329 | }; | ||
330 | module_mipi_dsi_driver(wuxga_nt_panel_driver); | ||
331 | |||
332 | MODULE_AUTHOR("Werner Johansson <werner.johansson@sonymobile.com>"); | ||
333 | MODULE_DESCRIPTION("Panasonic VVX10F034N00 Novatek NT1397-based WUXGA (1920x1200) video mode panel driver"); | ||
334 | MODULE_LICENSE("GPL v2"); | ||