diff options
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-ld9040.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-s6e8aa0.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c | 464 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 133 |
6 files changed, 605 insertions, 49 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index bee9f72b3a93..024e98ef8e4d 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig | |||
@@ -27,4 +27,17 @@ config DRM_PANEL_S6E8AA0 | |||
27 | select DRM_MIPI_DSI | 27 | select DRM_MIPI_DSI |
28 | select VIDEOMODE_HELPERS | 28 | select VIDEOMODE_HELPERS |
29 | 29 | ||
30 | config DRM_PANEL_SHARP_LQ101R1SX01 | ||
31 | tristate "Sharp LQ101R1SX01 panel" | ||
32 | depends on OF | ||
33 | depends on DRM_MIPI_DSI | ||
34 | help | ||
35 | Say Y here if you want to enable support for Sharp LQ101R1SX01 | ||
36 | TFT-LCD modules. The panel has a 2560x1600 resolution and uses | ||
37 | 24 bit RGB per pixel. It provides a dual MIPI DSI interface to | ||
38 | the host and has a built-in LED backlight. | ||
39 | |||
40 | To compile this driver as a module, choose M here: the module | ||
41 | will be called panel-sharp-lq101r1sx01. | ||
42 | |||
30 | endmenu | 43 | endmenu |
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 8b929212fad7..4b2a0430804b 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o | 1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
2 | obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o | 2 | obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o |
3 | obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o | 3 | obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o |
4 | obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o | ||
diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c index 42ac67b21e9f..08cf2c588c3d 100644 --- a/drivers/gpu/drm/panel/panel-ld9040.c +++ b/drivers/gpu/drm/panel/panel-ld9040.c | |||
@@ -145,7 +145,7 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) | |||
145 | if (ctx->error < 0 || len == 0) | 145 | if (ctx->error < 0 || len == 0) |
146 | return; | 146 | return; |
147 | 147 | ||
148 | dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", len, data); | 148 | dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", (int)len, data); |
149 | ret = ld9040_spi_write_word(ctx, *data); | 149 | ret = ld9040_spi_write_word(ctx, *data); |
150 | 150 | ||
151 | while (!ret && --len) { | 151 | while (!ret && --len) { |
@@ -154,8 +154,8 @@ static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) | |||
154 | } | 154 | } |
155 | 155 | ||
156 | if (ret) { | 156 | if (ret) { |
157 | dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, | 157 | dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, |
158 | data); | 158 | (int)len, data); |
159 | ctx->error = ret; | 159 | ctx->error = ret; |
160 | } | 160 | } |
161 | 161 | ||
@@ -336,17 +336,12 @@ static int ld9040_probe(struct spi_device *spi) | |||
336 | if (ret < 0) | 336 | if (ret < 0) |
337 | return ret; | 337 | return ret; |
338 | 338 | ||
339 | ctx->reset_gpio = devm_gpiod_get(dev, "reset"); | 339 | ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); |
340 | if (IS_ERR(ctx->reset_gpio)) { | 340 | if (IS_ERR(ctx->reset_gpio)) { |
341 | dev_err(dev, "cannot get reset-gpios %ld\n", | 341 | dev_err(dev, "cannot get reset-gpios %ld\n", |
342 | PTR_ERR(ctx->reset_gpio)); | 342 | PTR_ERR(ctx->reset_gpio)); |
343 | return PTR_ERR(ctx->reset_gpio); | 343 | return PTR_ERR(ctx->reset_gpio); |
344 | } | 344 | } |
345 | ret = gpiod_direction_output(ctx->reset_gpio, 1); | ||
346 | if (ret < 0) { | ||
347 | dev_err(dev, "cannot configure reset-gpios %d\n", ret); | ||
348 | return ret; | ||
349 | } | ||
350 | 345 | ||
351 | spi->bits_per_word = 9; | 346 | spi->bits_per_word = 9; |
352 | ret = spi_setup(spi); | 347 | ret = spi_setup(spi); |
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index b5217fe37f02..144b2733e3d7 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c | |||
@@ -141,10 +141,10 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) | |||
141 | if (ctx->error < 0) | 141 | if (ctx->error < 0) |
142 | return; | 142 | return; |
143 | 143 | ||
144 | ret = mipi_dsi_dcs_write(dsi, data, len); | 144 | ret = mipi_dsi_dcs_write_buffer(dsi, data, len); |
145 | if (ret < 0) { | 145 | if (ret < 0) { |
146 | dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, | 146 | dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, |
147 | data); | 147 | (int)len, data); |
148 | ctx->error = ret; | 148 | ctx->error = ret; |
149 | } | 149 | } |
150 | } | 150 | } |
@@ -800,27 +800,15 @@ static void s6e8aa0_panel_init(struct s6e8aa0 *ctx) | |||
800 | } | 800 | } |
801 | 801 | ||
802 | static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx, | 802 | static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx, |
803 | int size) | 803 | u16 size) |
804 | { | 804 | { |
805 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | 805 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
806 | const struct mipi_dsi_host_ops *ops = dsi->host->ops; | ||
807 | u8 buf[] = {size, 0}; | ||
808 | struct mipi_dsi_msg msg = { | ||
809 | .channel = dsi->channel, | ||
810 | .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, | ||
811 | .tx_len = sizeof(buf), | ||
812 | .tx_buf = buf | ||
813 | }; | ||
814 | int ret; | 806 | int ret; |
815 | 807 | ||
816 | if (ctx->error < 0) | 808 | if (ctx->error < 0) |
817 | return; | 809 | return; |
818 | 810 | ||
819 | if (!ops || !ops->transfer) | 811 | ret = mipi_dsi_set_maximum_return_packet_size(dsi, size); |
820 | ret = -EIO; | ||
821 | else | ||
822 | ret = ops->transfer(dsi->host, &msg); | ||
823 | |||
824 | if (ret < 0) { | 812 | if (ret < 0) { |
825 | dev_err(ctx->dev, | 813 | dev_err(ctx->dev, |
826 | "error %d setting maximum return packet size to %d\n", | 814 | "error %d setting maximum return packet size to %d\n", |
@@ -1019,17 +1007,12 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) | |||
1019 | return ret; | 1007 | return ret; |
1020 | } | 1008 | } |
1021 | 1009 | ||
1022 | ctx->reset_gpio = devm_gpiod_get(dev, "reset"); | 1010 | ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); |
1023 | if (IS_ERR(ctx->reset_gpio)) { | 1011 | if (IS_ERR(ctx->reset_gpio)) { |
1024 | dev_err(dev, "cannot get reset-gpios %ld\n", | 1012 | dev_err(dev, "cannot get reset-gpios %ld\n", |
1025 | PTR_ERR(ctx->reset_gpio)); | 1013 | PTR_ERR(ctx->reset_gpio)); |
1026 | return PTR_ERR(ctx->reset_gpio); | 1014 | return PTR_ERR(ctx->reset_gpio); |
1027 | } | 1015 | } |
1028 | ret = gpiod_direction_output(ctx->reset_gpio, 1); | ||
1029 | if (ret < 0) { | ||
1030 | dev_err(dev, "cannot configure reset-gpios %d\n", ret); | ||
1031 | return ret; | ||
1032 | } | ||
1033 | 1016 | ||
1034 | ctx->brightness = GAMMA_LEVEL_NUM - 1; | 1017 | ctx->brightness = GAMMA_LEVEL_NUM - 1; |
1035 | 1018 | ||
@@ -1069,7 +1052,6 @@ static struct mipi_dsi_driver s6e8aa0_driver = { | |||
1069 | .remove = s6e8aa0_remove, | 1052 | .remove = s6e8aa0_remove, |
1070 | .driver = { | 1053 | .driver = { |
1071 | .name = "panel_s6e8aa0", | 1054 | .name = "panel_s6e8aa0", |
1072 | .owner = THIS_MODULE, | ||
1073 | .of_match_table = s6e8aa0_of_match, | 1055 | .of_match_table = s6e8aa0_of_match, |
1074 | }, | 1056 | }, |
1075 | }; | 1057 | }; |
diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c new file mode 100644 index 000000000000..9d81759d82fc --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 NVIDIA Corporation | ||
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 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/backlight.h> | ||
10 | #include <linux/gpio/consumer.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/of.h> | ||
13 | #include <linux/regulator/consumer.h> | ||
14 | |||
15 | #include <drm/drmP.h> | ||
16 | #include <drm/drm_crtc.h> | ||
17 | #include <drm/drm_mipi_dsi.h> | ||
18 | #include <drm/drm_panel.h> | ||
19 | |||
20 | #include <video/mipi_display.h> | ||
21 | |||
22 | #include <linux/host1x.h> | ||
23 | |||
24 | struct sharp_panel { | ||
25 | struct drm_panel base; | ||
26 | /* the datasheet refers to them as DSI-LINK1 and DSI-LINK2 */ | ||
27 | struct mipi_dsi_device *link1; | ||
28 | struct mipi_dsi_device *link2; | ||
29 | |||
30 | struct backlight_device *backlight; | ||
31 | struct regulator *supply; | ||
32 | |||
33 | bool prepared; | ||
34 | bool enabled; | ||
35 | |||
36 | const struct drm_display_mode *mode; | ||
37 | }; | ||
38 | |||
39 | static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel) | ||
40 | { | ||
41 | return container_of(panel, struct sharp_panel, base); | ||
42 | } | ||
43 | |||
44 | static int sharp_panel_write(struct sharp_panel *sharp, u16 offset, u8 value) | ||
45 | { | ||
46 | u8 payload[3] = { offset >> 8, offset & 0xff, value }; | ||
47 | struct mipi_dsi_device *dsi = sharp->link1; | ||
48 | ssize_t err; | ||
49 | |||
50 | err = mipi_dsi_generic_write(dsi, payload, sizeof(payload)); | ||
51 | if (err < 0) { | ||
52 | dev_err(&dsi->dev, "failed to write %02x to %04x: %zd\n", | ||
53 | value, offset, err); | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | err = mipi_dsi_dcs_nop(dsi); | ||
58 | if (err < 0) { | ||
59 | dev_err(&dsi->dev, "failed to send DCS nop: %zd\n", err); | ||
60 | return err; | ||
61 | } | ||
62 | |||
63 | usleep_range(10, 20); | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static __maybe_unused int sharp_panel_read(struct sharp_panel *sharp, | ||
69 | u16 offset, u8 *value) | ||
70 | { | ||
71 | ssize_t err; | ||
72 | |||
73 | cpu_to_be16s(&offset); | ||
74 | |||
75 | err = mipi_dsi_generic_read(sharp->link1, &offset, sizeof(offset), | ||
76 | value, sizeof(*value)); | ||
77 | if (err < 0) | ||
78 | dev_err(&sharp->link1->dev, "failed to read from %04x: %zd\n", | ||
79 | offset, err); | ||
80 | |||
81 | return err; | ||
82 | } | ||
83 | |||
84 | static int sharp_panel_disable(struct drm_panel *panel) | ||
85 | { | ||
86 | struct sharp_panel *sharp = to_sharp_panel(panel); | ||
87 | |||
88 | if (!sharp->enabled) | ||
89 | return 0; | ||
90 | |||
91 | if (sharp->backlight) { | ||
92 | sharp->backlight->props.power = FB_BLANK_POWERDOWN; | ||
93 | backlight_update_status(sharp->backlight); | ||
94 | } | ||
95 | |||
96 | sharp->enabled = false; | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static int sharp_panel_unprepare(struct drm_panel *panel) | ||
102 | { | ||
103 | struct sharp_panel *sharp = to_sharp_panel(panel); | ||
104 | int err; | ||
105 | |||
106 | if (!sharp->prepared) | ||
107 | return 0; | ||
108 | |||
109 | err = mipi_dsi_dcs_set_display_off(sharp->link1); | ||
110 | if (err < 0) | ||
111 | dev_err(panel->dev, "failed to set display off: %d\n", err); | ||
112 | |||
113 | err = mipi_dsi_dcs_enter_sleep_mode(sharp->link1); | ||
114 | if (err < 0) | ||
115 | dev_err(panel->dev, "failed to enter sleep mode: %d\n", err); | ||
116 | |||
117 | msleep(120); | ||
118 | |||
119 | regulator_disable(sharp->supply); | ||
120 | |||
121 | sharp->prepared = false; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int sharp_setup_symmetrical_split(struct mipi_dsi_device *left, | ||
127 | struct mipi_dsi_device *right, | ||
128 | const struct drm_display_mode *mode) | ||
129 | { | ||
130 | int err; | ||
131 | |||
132 | err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1); | ||
133 | if (err < 0) { | ||
134 | dev_err(&left->dev, "failed to set column address: %d\n", err); | ||
135 | return err; | ||
136 | } | ||
137 | |||
138 | err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1); | ||
139 | if (err < 0) { | ||
140 | dev_err(&left->dev, "failed to set page address: %d\n", err); | ||
141 | return err; | ||
142 | } | ||
143 | |||
144 | err = mipi_dsi_dcs_set_column_address(right, mode->hdisplay / 2, | ||
145 | mode->hdisplay - 1); | ||
146 | if (err < 0) { | ||
147 | dev_err(&right->dev, "failed to set column address: %d\n", err); | ||
148 | return err; | ||
149 | } | ||
150 | |||
151 | err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1); | ||
152 | if (err < 0) { | ||
153 | dev_err(&right->dev, "failed to set page address: %d\n", err); | ||
154 | return err; | ||
155 | } | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int sharp_panel_prepare(struct drm_panel *panel) | ||
161 | { | ||
162 | struct sharp_panel *sharp = to_sharp_panel(panel); | ||
163 | u8 format = MIPI_DCS_PIXEL_FMT_24BIT; | ||
164 | int err; | ||
165 | |||
166 | if (sharp->prepared) | ||
167 | return 0; | ||
168 | |||
169 | err = regulator_enable(sharp->supply); | ||
170 | if (err < 0) | ||
171 | return err; | ||
172 | |||
173 | usleep_range(10000, 20000); | ||
174 | |||
175 | err = mipi_dsi_dcs_soft_reset(sharp->link1); | ||
176 | if (err < 0) { | ||
177 | dev_err(panel->dev, "soft reset failed: %d\n", err); | ||
178 | goto poweroff; | ||
179 | } | ||
180 | |||
181 | msleep(120); | ||
182 | |||
183 | err = mipi_dsi_dcs_exit_sleep_mode(sharp->link1); | ||
184 | if (err < 0) { | ||
185 | dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); | ||
186 | goto poweroff; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * The MIPI DCS specification mandates this delay only between the | ||
191 | * exit_sleep_mode and enter_sleep_mode commands, so it isn't strictly | ||
192 | * necessary here. | ||
193 | */ | ||
194 | /* | ||
195 | msleep(120); | ||
196 | */ | ||
197 | |||
198 | /* set left-right mode */ | ||
199 | err = sharp_panel_write(sharp, 0x1000, 0x2a); | ||
200 | if (err < 0) { | ||
201 | dev_err(panel->dev, "failed to set left-right mode: %d\n", err); | ||
202 | goto poweroff; | ||
203 | } | ||
204 | |||
205 | /* enable command mode */ | ||
206 | err = sharp_panel_write(sharp, 0x1001, 0x01); | ||
207 | if (err < 0) { | ||
208 | dev_err(panel->dev, "failed to enable command mode: %d\n", err); | ||
209 | goto poweroff; | ||
210 | } | ||
211 | |||
212 | err = mipi_dsi_dcs_set_pixel_format(sharp->link1, format); | ||
213 | if (err < 0) { | ||
214 | dev_err(panel->dev, "failed to set pixel format: %d\n", err); | ||
215 | goto poweroff; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * TODO: The device supports both left-right and even-odd split | ||
220 | * configurations, but this driver currently supports only the left- | ||
221 | * right split. To support a different mode a mechanism needs to be | ||
222 | * put in place to communicate the configuration back to the DSI host | ||
223 | * controller. | ||
224 | */ | ||
225 | err = sharp_setup_symmetrical_split(sharp->link1, sharp->link2, | ||
226 | sharp->mode); | ||
227 | if (err < 0) { | ||
228 | dev_err(panel->dev, "failed to set up symmetrical split: %d\n", | ||
229 | err); | ||
230 | goto poweroff; | ||
231 | } | ||
232 | |||
233 | err = mipi_dsi_dcs_set_display_on(sharp->link1); | ||
234 | if (err < 0) { | ||
235 | dev_err(panel->dev, "failed to set display on: %d\n", err); | ||
236 | goto poweroff; | ||
237 | } | ||
238 | |||
239 | sharp->prepared = true; | ||
240 | |||
241 | return 0; | ||
242 | |||
243 | poweroff: | ||
244 | regulator_disable(sharp->supply); | ||
245 | return err; | ||
246 | } | ||
247 | |||
248 | static int sharp_panel_enable(struct drm_panel *panel) | ||
249 | { | ||
250 | struct sharp_panel *sharp = to_sharp_panel(panel); | ||
251 | |||
252 | if (sharp->enabled) | ||
253 | return 0; | ||
254 | |||
255 | if (sharp->backlight) { | ||
256 | sharp->backlight->props.power = FB_BLANK_UNBLANK; | ||
257 | backlight_update_status(sharp->backlight); | ||
258 | } | ||
259 | |||
260 | sharp->enabled = true; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static const struct drm_display_mode default_mode = { | ||
266 | .clock = 278000, | ||
267 | .hdisplay = 2560, | ||
268 | .hsync_start = 2560 + 128, | ||
269 | .hsync_end = 2560 + 128 + 64, | ||
270 | .htotal = 2560 + 128 + 64 + 64, | ||
271 | .vdisplay = 1600, | ||
272 | .vsync_start = 1600 + 4, | ||
273 | .vsync_end = 1600 + 4 + 8, | ||
274 | .vtotal = 1600 + 4 + 8 + 32, | ||
275 | .vrefresh = 60, | ||
276 | }; | ||
277 | |||
278 | static int sharp_panel_get_modes(struct drm_panel *panel) | ||
279 | { | ||
280 | struct drm_display_mode *mode; | ||
281 | |||
282 | mode = drm_mode_duplicate(panel->drm, &default_mode); | ||
283 | if (!mode) { | ||
284 | dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n", | ||
285 | default_mode.hdisplay, default_mode.vdisplay, | ||
286 | default_mode.vrefresh); | ||
287 | return -ENOMEM; | ||
288 | } | ||
289 | |||
290 | drm_mode_set_name(mode); | ||
291 | |||
292 | drm_mode_probed_add(panel->connector, mode); | ||
293 | |||
294 | panel->connector->display_info.width_mm = 217; | ||
295 | panel->connector->display_info.height_mm = 136; | ||
296 | |||
297 | return 1; | ||
298 | } | ||
299 | |||
300 | static const struct drm_panel_funcs sharp_panel_funcs = { | ||
301 | .disable = sharp_panel_disable, | ||
302 | .unprepare = sharp_panel_unprepare, | ||
303 | .prepare = sharp_panel_prepare, | ||
304 | .enable = sharp_panel_enable, | ||
305 | .get_modes = sharp_panel_get_modes, | ||
306 | }; | ||
307 | |||
308 | static const struct of_device_id sharp_of_match[] = { | ||
309 | { .compatible = "sharp,lq101r1sx01", }, | ||
310 | { } | ||
311 | }; | ||
312 | MODULE_DEVICE_TABLE(of, sharp_of_match); | ||
313 | |||
314 | static int sharp_panel_add(struct sharp_panel *sharp) | ||
315 | { | ||
316 | struct device_node *np; | ||
317 | int err; | ||
318 | |||
319 | sharp->mode = &default_mode; | ||
320 | |||
321 | sharp->supply = devm_regulator_get(&sharp->link1->dev, "power"); | ||
322 | if (IS_ERR(sharp->supply)) | ||
323 | return PTR_ERR(sharp->supply); | ||
324 | |||
325 | np = of_parse_phandle(sharp->link1->dev.of_node, "backlight", 0); | ||
326 | if (np) { | ||
327 | sharp->backlight = of_find_backlight_by_node(np); | ||
328 | of_node_put(np); | ||
329 | |||
330 | if (!sharp->backlight) | ||
331 | return -EPROBE_DEFER; | ||
332 | } | ||
333 | |||
334 | drm_panel_init(&sharp->base); | ||
335 | sharp->base.funcs = &sharp_panel_funcs; | ||
336 | sharp->base.dev = &sharp->link1->dev; | ||
337 | |||
338 | err = drm_panel_add(&sharp->base); | ||
339 | if (err < 0) | ||
340 | goto put_backlight; | ||
341 | |||
342 | return 0; | ||
343 | |||
344 | put_backlight: | ||
345 | if (sharp->backlight) | ||
346 | put_device(&sharp->backlight->dev); | ||
347 | |||
348 | return err; | ||
349 | } | ||
350 | |||
351 | static void sharp_panel_del(struct sharp_panel *sharp) | ||
352 | { | ||
353 | if (sharp->base.dev) | ||
354 | drm_panel_remove(&sharp->base); | ||
355 | |||
356 | if (sharp->backlight) | ||
357 | put_device(&sharp->backlight->dev); | ||
358 | |||
359 | if (sharp->link2) | ||
360 | put_device(&sharp->link2->dev); | ||
361 | } | ||
362 | |||
363 | static int sharp_panel_probe(struct mipi_dsi_device *dsi) | ||
364 | { | ||
365 | struct mipi_dsi_device *secondary = NULL; | ||
366 | struct sharp_panel *sharp; | ||
367 | struct device_node *np; | ||
368 | int err; | ||
369 | |||
370 | dsi->lanes = 4; | ||
371 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
372 | dsi->mode_flags = MIPI_DSI_MODE_LPM; | ||
373 | |||
374 | /* Find DSI-LINK1 */ | ||
375 | np = of_parse_phandle(dsi->dev.of_node, "link2", 0); | ||
376 | if (np) { | ||
377 | secondary = of_find_mipi_dsi_device_by_node(np); | ||
378 | of_node_put(np); | ||
379 | |||
380 | if (!secondary) | ||
381 | return -EPROBE_DEFER; | ||
382 | } | ||
383 | |||
384 | /* register a panel for only the DSI-LINK1 interface */ | ||
385 | if (secondary) { | ||
386 | sharp = devm_kzalloc(&dsi->dev, sizeof(*sharp), GFP_KERNEL); | ||
387 | if (!sharp) { | ||
388 | put_device(&secondary->dev); | ||
389 | return -ENOMEM; | ||
390 | } | ||
391 | |||
392 | mipi_dsi_set_drvdata(dsi, sharp); | ||
393 | |||
394 | sharp->link2 = secondary; | ||
395 | sharp->link1 = dsi; | ||
396 | |||
397 | err = sharp_panel_add(sharp); | ||
398 | if (err < 0) { | ||
399 | put_device(&secondary->dev); | ||
400 | return err; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | err = mipi_dsi_attach(dsi); | ||
405 | if (err < 0) { | ||
406 | if (secondary) | ||
407 | sharp_panel_del(sharp); | ||
408 | |||
409 | return err; | ||
410 | } | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int sharp_panel_remove(struct mipi_dsi_device *dsi) | ||
416 | { | ||
417 | struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); | ||
418 | int err; | ||
419 | |||
420 | /* only detach from host for the DSI-LINK2 interface */ | ||
421 | if (!sharp) { | ||
422 | mipi_dsi_detach(dsi); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | err = sharp_panel_disable(&sharp->base); | ||
427 | if (err < 0) | ||
428 | dev_err(&dsi->dev, "failed to disable panel: %d\n", err); | ||
429 | |||
430 | err = mipi_dsi_detach(dsi); | ||
431 | if (err < 0) | ||
432 | dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); | ||
433 | |||
434 | drm_panel_detach(&sharp->base); | ||
435 | sharp_panel_del(sharp); | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static void sharp_panel_shutdown(struct mipi_dsi_device *dsi) | ||
441 | { | ||
442 | struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); | ||
443 | |||
444 | /* nothing to do for DSI-LINK2 */ | ||
445 | if (!sharp) | ||
446 | return; | ||
447 | |||
448 | sharp_panel_disable(&sharp->base); | ||
449 | } | ||
450 | |||
451 | static struct mipi_dsi_driver sharp_panel_driver = { | ||
452 | .driver = { | ||
453 | .name = "panel-sharp-lq101r1sx01", | ||
454 | .of_match_table = sharp_of_match, | ||
455 | }, | ||
456 | .probe = sharp_panel_probe, | ||
457 | .remove = sharp_panel_remove, | ||
458 | .shutdown = sharp_panel_shutdown, | ||
459 | }; | ||
460 | module_mipi_dsi_driver(sharp_panel_driver); | ||
461 | |||
462 | MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); | ||
463 | MODULE_DESCRIPTION("Sharp LQ101R1SX01 panel driver"); | ||
464 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 12bc8a0ab1cf..e95385bf8356 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c | |||
@@ -247,21 +247,14 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) | |||
247 | if (IS_ERR(panel->supply)) | 247 | if (IS_ERR(panel->supply)) |
248 | return PTR_ERR(panel->supply); | 248 | return PTR_ERR(panel->supply); |
249 | 249 | ||
250 | panel->enable_gpio = devm_gpiod_get_optional(dev, "enable"); | 250 | panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", |
251 | GPIOD_OUT_LOW); | ||
251 | if (IS_ERR(panel->enable_gpio)) { | 252 | if (IS_ERR(panel->enable_gpio)) { |
252 | err = PTR_ERR(panel->enable_gpio); | 253 | err = PTR_ERR(panel->enable_gpio); |
253 | dev_err(dev, "failed to request GPIO: %d\n", err); | 254 | dev_err(dev, "failed to request GPIO: %d\n", err); |
254 | return err; | 255 | return err; |
255 | } | 256 | } |
256 | 257 | ||
257 | if (panel->enable_gpio) { | ||
258 | err = gpiod_direction_output(panel->enable_gpio, 0); | ||
259 | if (err < 0) { | ||
260 | dev_err(dev, "failed to setup GPIO: %d\n", err); | ||
261 | return err; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | backlight = of_parse_phandle(dev->of_node, "backlight", 0); | 258 | backlight = of_parse_phandle(dev->of_node, "backlight", 0); |
266 | if (backlight) { | 259 | if (backlight) { |
267 | panel->backlight = of_find_backlight_by_node(backlight); | 260 | panel->backlight = of_find_backlight_by_node(backlight); |
@@ -376,6 +369,29 @@ static const struct panel_desc auo_b101xtn01 = { | |||
376 | }, | 369 | }, |
377 | }; | 370 | }; |
378 | 371 | ||
372 | static const struct drm_display_mode auo_b116xw03_mode = { | ||
373 | .clock = 70589, | ||
374 | .hdisplay = 1366, | ||
375 | .hsync_start = 1366 + 40, | ||
376 | .hsync_end = 1366 + 40 + 40, | ||
377 | .htotal = 1366 + 40 + 40 + 32, | ||
378 | .vdisplay = 768, | ||
379 | .vsync_start = 768 + 10, | ||
380 | .vsync_end = 768 + 10 + 12, | ||
381 | .vtotal = 768 + 10 + 12 + 6, | ||
382 | .vrefresh = 60, | ||
383 | }; | ||
384 | |||
385 | static const struct panel_desc auo_b116xw03 = { | ||
386 | .modes = &auo_b116xw03_mode, | ||
387 | .num_modes = 1, | ||
388 | .bpc = 6, | ||
389 | .size = { | ||
390 | .width = 256, | ||
391 | .height = 144, | ||
392 | }, | ||
393 | }; | ||
394 | |||
379 | static const struct drm_display_mode auo_b133xtn01_mode = { | 395 | static const struct drm_display_mode auo_b133xtn01_mode = { |
380 | .clock = 69500, | 396 | .clock = 69500, |
381 | .hdisplay = 1366, | 397 | .hdisplay = 1366, |
@@ -415,6 +431,7 @@ static const struct drm_display_mode auo_b133htn01_mode = { | |||
415 | static const struct panel_desc auo_b133htn01 = { | 431 | static const struct panel_desc auo_b133htn01 = { |
416 | .modes = &auo_b133htn01_mode, | 432 | .modes = &auo_b133htn01_mode, |
417 | .num_modes = 1, | 433 | .num_modes = 1, |
434 | .bpc = 6, | ||
418 | .size = { | 435 | .size = { |
419 | .width = 293, | 436 | .width = 293, |
420 | .height = 165, | 437 | .height = 165, |
@@ -536,22 +553,92 @@ static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { | |||
536 | static const struct panel_desc foxlink_fl500wvr00_a0t = { | 553 | static const struct panel_desc foxlink_fl500wvr00_a0t = { |
537 | .modes = &foxlink_fl500wvr00_a0t_mode, | 554 | .modes = &foxlink_fl500wvr00_a0t_mode, |
538 | .num_modes = 1, | 555 | .num_modes = 1, |
556 | .bpc = 8, | ||
539 | .size = { | 557 | .size = { |
540 | .width = 108, | 558 | .width = 108, |
541 | .height = 65, | 559 | .height = 65, |
542 | }, | 560 | }, |
543 | }; | 561 | }; |
544 | 562 | ||
545 | static const struct drm_display_mode innolux_n116bge_mode = { | 563 | static const struct drm_display_mode hannstar_hsd070pww1_mode = { |
564 | .clock = 71100, | ||
565 | .hdisplay = 1280, | ||
566 | .hsync_start = 1280 + 1, | ||
567 | .hsync_end = 1280 + 1 + 158, | ||
568 | .htotal = 1280 + 1 + 158 + 1, | ||
569 | .vdisplay = 800, | ||
570 | .vsync_start = 800 + 1, | ||
571 | .vsync_end = 800 + 1 + 21, | ||
572 | .vtotal = 800 + 1 + 21 + 1, | ||
573 | .vrefresh = 60, | ||
574 | }; | ||
575 | |||
576 | static const struct panel_desc hannstar_hsd070pww1 = { | ||
577 | .modes = &hannstar_hsd070pww1_mode, | ||
578 | .num_modes = 1, | ||
579 | .bpc = 6, | ||
580 | .size = { | ||
581 | .width = 151, | ||
582 | .height = 94, | ||
583 | }, | ||
584 | }; | ||
585 | |||
586 | static const struct drm_display_mode hitachi_tx23d38vm0caa_mode = { | ||
587 | .clock = 33333, | ||
588 | .hdisplay = 800, | ||
589 | .hsync_start = 800 + 85, | ||
590 | .hsync_end = 800 + 85 + 86, | ||
591 | .htotal = 800 + 85 + 86 + 85, | ||
592 | .vdisplay = 480, | ||
593 | .vsync_start = 480 + 16, | ||
594 | .vsync_end = 480 + 16 + 13, | ||
595 | .vtotal = 480 + 16 + 13 + 16, | ||
596 | .vrefresh = 60, | ||
597 | }; | ||
598 | |||
599 | static const struct panel_desc hitachi_tx23d38vm0caa = { | ||
600 | .modes = &hitachi_tx23d38vm0caa_mode, | ||
601 | .num_modes = 1, | ||
602 | .bpc = 6, | ||
603 | .size = { | ||
604 | .width = 195, | ||
605 | .height = 117, | ||
606 | }, | ||
607 | }; | ||
608 | |||
609 | static const struct drm_display_mode innolux_g121i1_l01_mode = { | ||
546 | .clock = 71000, | 610 | .clock = 71000, |
611 | .hdisplay = 1280, | ||
612 | .hsync_start = 1280 + 64, | ||
613 | .hsync_end = 1280 + 64 + 32, | ||
614 | .htotal = 1280 + 64 + 32 + 64, | ||
615 | .vdisplay = 800, | ||
616 | .vsync_start = 800 + 9, | ||
617 | .vsync_end = 800 + 9 + 6, | ||
618 | .vtotal = 800 + 9 + 6 + 9, | ||
619 | .vrefresh = 60, | ||
620 | }; | ||
621 | |||
622 | static const struct panel_desc innolux_g121i1_l01 = { | ||
623 | .modes = &innolux_g121i1_l01_mode, | ||
624 | .num_modes = 1, | ||
625 | .bpc = 6, | ||
626 | .size = { | ||
627 | .width = 261, | ||
628 | .height = 163, | ||
629 | }, | ||
630 | }; | ||
631 | |||
632 | static const struct drm_display_mode innolux_n116bge_mode = { | ||
633 | .clock = 76420, | ||
547 | .hdisplay = 1366, | 634 | .hdisplay = 1366, |
548 | .hsync_start = 1366 + 64, | 635 | .hsync_start = 1366 + 136, |
549 | .hsync_end = 1366 + 64 + 6, | 636 | .hsync_end = 1366 + 136 + 30, |
550 | .htotal = 1366 + 64 + 6 + 64, | 637 | .htotal = 1366 + 136 + 30 + 60, |
551 | .vdisplay = 768, | 638 | .vdisplay = 768, |
552 | .vsync_start = 768 + 8, | 639 | .vsync_start = 768 + 8, |
553 | .vsync_end = 768 + 8 + 4, | 640 | .vsync_end = 768 + 8 + 12, |
554 | .vtotal = 768 + 8 + 4 + 8, | 641 | .vtotal = 768 + 8 + 12 + 12, |
555 | .vrefresh = 60, | 642 | .vrefresh = 60, |
556 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | 643 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
557 | }; | 644 | }; |
@@ -643,6 +730,9 @@ static const struct of_device_id platform_of_match[] = { | |||
643 | .compatible = "auo,b101xtn01", | 730 | .compatible = "auo,b101xtn01", |
644 | .data = &auo_b101xtn01, | 731 | .data = &auo_b101xtn01, |
645 | }, { | 732 | }, { |
733 | .compatible = "auo,b116xw03", | ||
734 | .data = &auo_b116xw03, | ||
735 | }, { | ||
646 | .compatible = "auo,b133htn01", | 736 | .compatible = "auo,b133htn01", |
647 | .data = &auo_b133htn01, | 737 | .data = &auo_b133htn01, |
648 | }, { | 738 | }, { |
@@ -667,6 +757,15 @@ static const struct of_device_id platform_of_match[] = { | |||
667 | .compatible = "foxlink,fl500wvr00-a0t", | 757 | .compatible = "foxlink,fl500wvr00-a0t", |
668 | .data = &foxlink_fl500wvr00_a0t, | 758 | .data = &foxlink_fl500wvr00_a0t, |
669 | }, { | 759 | }, { |
760 | .compatible = "hannstar,hsd070pww1", | ||
761 | .data = &hannstar_hsd070pww1, | ||
762 | }, { | ||
763 | .compatible = "hit,tx23d38vm0caa", | ||
764 | .data = &hitachi_tx23d38vm0caa | ||
765 | }, { | ||
766 | .compatible ="innolux,g121i1-l01", | ||
767 | .data = &innolux_g121i1_l01 | ||
768 | }, { | ||
670 | .compatible = "innolux,n116bge", | 769 | .compatible = "innolux,n116bge", |
671 | .data = &innolux_n116bge, | 770 | .data = &innolux_n116bge, |
672 | }, { | 771 | }, { |
@@ -740,6 +839,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = { | |||
740 | .desc = { | 839 | .desc = { |
741 | .modes = &lg_ld070wx3_sl01_mode, | 840 | .modes = &lg_ld070wx3_sl01_mode, |
742 | .num_modes = 1, | 841 | .num_modes = 1, |
842 | .bpc = 8, | ||
743 | .size = { | 843 | .size = { |
744 | .width = 94, | 844 | .width = 94, |
745 | .height = 151, | 845 | .height = 151, |
@@ -767,6 +867,7 @@ static const struct panel_desc_dsi lg_lh500wx1_sd03 = { | |||
767 | .desc = { | 867 | .desc = { |
768 | .modes = &lg_lh500wx1_sd03_mode, | 868 | .modes = &lg_lh500wx1_sd03_mode, |
769 | .num_modes = 1, | 869 | .num_modes = 1, |
870 | .bpc = 8, | ||
770 | .size = { | 871 | .size = { |
771 | .width = 62, | 872 | .width = 62, |
772 | .height = 110, | 873 | .height = 110, |
@@ -794,6 +895,7 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = { | |||
794 | .desc = { | 895 | .desc = { |
795 | .modes = &panasonic_vvx10f004b00_mode, | 896 | .modes = &panasonic_vvx10f004b00_mode, |
796 | .num_modes = 1, | 897 | .num_modes = 1, |
898 | .bpc = 8, | ||
797 | .size = { | 899 | .size = { |
798 | .width = 217, | 900 | .width = 217, |
799 | .height = 136, | 901 | .height = 136, |
@@ -863,7 +965,6 @@ static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi) | |||
863 | static struct mipi_dsi_driver panel_simple_dsi_driver = { | 965 | static struct mipi_dsi_driver panel_simple_dsi_driver = { |
864 | .driver = { | 966 | .driver = { |
865 | .name = "panel-simple-dsi", | 967 | .name = "panel-simple-dsi", |
866 | .owner = THIS_MODULE, | ||
867 | .of_match_table = dsi_of_match, | 968 | .of_match_table = dsi_of_match, |
868 | }, | 969 | }, |
869 | .probe = panel_simple_dsi_probe, | 970 | .probe = panel_simple_dsi_probe, |