]>
Commit | Line | Data |
---|---|---|
2dab3bf8 | 1 | // SPDX-License-Identifier: GPL-2.0 |
f0a5bb98 PC |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2017 | |
4 | * | |
5 | * Authors: Philippe Cornu <philippe.cornu@st.com> | |
6 | * Yannick Fertre <yannick.fertre@st.com> | |
f0a5bb98 | 7 | */ |
2dab3bf8 | 8 | |
f0a5bb98 PC |
9 | #include <drm/drmP.h> |
10 | #include <drm/drm_mipi_dsi.h> | |
11 | #include <drm/drm_panel.h> | |
12 | #include <linux/backlight.h> | |
13 | #include <linux/gpio/consumer.h> | |
ded8d7fe | 14 | #include <linux/regulator/consumer.h> |
f0a5bb98 PC |
15 | #include <video/mipi_display.h> |
16 | ||
f0a5bb98 PC |
17 | #define OTM8009A_BACKLIGHT_DEFAULT 240 |
18 | #define OTM8009A_BACKLIGHT_MAX 255 | |
19 | ||
20 | /* Manufacturer Command Set */ | |
21 | #define MCS_ADRSFT 0x0000 /* Address Shift Function */ | |
22 | #define MCS_PANSET 0xB3A6 /* Panel Type Setting */ | |
23 | #define MCS_SD_CTRL 0xC0A2 /* Source Driver Timing Setting */ | |
24 | #define MCS_P_DRV_M 0xC0B4 /* Panel Driving Mode */ | |
25 | #define MCS_OSC_ADJ 0xC181 /* Oscillator Adjustment for Idle/Normal mode */ | |
26 | #define MCS_RGB_VID_SET 0xC1A1 /* RGB Video Mode Setting */ | |
27 | #define MCS_SD_PCH_CTRL 0xC480 /* Source Driver Precharge Control */ | |
28 | #define MCS_NO_DOC1 0xC48A /* Command not documented */ | |
29 | #define MCS_PWR_CTRL1 0xC580 /* Power Control Setting 1 */ | |
30 | #define MCS_PWR_CTRL2 0xC590 /* Power Control Setting 2 for Normal Mode */ | |
31 | #define MCS_PWR_CTRL4 0xC5B0 /* Power Control Setting 4 for DC Voltage */ | |
32 | #define MCS_PANCTRLSET1 0xCB80 /* Panel Control Setting 1 */ | |
33 | #define MCS_PANCTRLSET2 0xCB90 /* Panel Control Setting 2 */ | |
34 | #define MCS_PANCTRLSET3 0xCBA0 /* Panel Control Setting 3 */ | |
35 | #define MCS_PANCTRLSET4 0xCBB0 /* Panel Control Setting 4 */ | |
36 | #define MCS_PANCTRLSET5 0xCBC0 /* Panel Control Setting 5 */ | |
37 | #define MCS_PANCTRLSET6 0xCBD0 /* Panel Control Setting 6 */ | |
38 | #define MCS_PANCTRLSET7 0xCBE0 /* Panel Control Setting 7 */ | |
39 | #define MCS_PANCTRLSET8 0xCBF0 /* Panel Control Setting 8 */ | |
40 | #define MCS_PANU2D1 0xCC80 /* Panel U2D Setting 1 */ | |
41 | #define MCS_PANU2D2 0xCC90 /* Panel U2D Setting 2 */ | |
42 | #define MCS_PANU2D3 0xCCA0 /* Panel U2D Setting 3 */ | |
43 | #define MCS_PAND2U1 0xCCB0 /* Panel D2U Setting 1 */ | |
44 | #define MCS_PAND2U2 0xCCC0 /* Panel D2U Setting 2 */ | |
45 | #define MCS_PAND2U3 0xCCD0 /* Panel D2U Setting 3 */ | |
46 | #define MCS_GOAVST 0xCE80 /* GOA VST Setting */ | |
47 | #define MCS_GOACLKA1 0xCEA0 /* GOA CLKA1 Setting */ | |
48 | #define MCS_GOACLKA3 0xCEB0 /* GOA CLKA3 Setting */ | |
49 | #define MCS_GOAECLK 0xCFC0 /* GOA ECLK Setting */ | |
50 | #define MCS_NO_DOC2 0xCFD0 /* Command not documented */ | |
51 | #define MCS_GVDDSET 0xD800 /* GVDD/NGVDD */ | |
52 | #define MCS_VCOMDC 0xD900 /* VCOM Voltage Setting */ | |
53 | #define MCS_GMCT2_2P 0xE100 /* Gamma Correction 2.2+ Setting */ | |
54 | #define MCS_GMCT2_2N 0xE200 /* Gamma Correction 2.2- Setting */ | |
55 | #define MCS_NO_DOC3 0xF5B6 /* Command not documented */ | |
56 | #define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ | |
57 | #define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ | |
58 | ||
59 | struct otm8009a { | |
60 | struct device *dev; | |
61 | struct drm_panel panel; | |
62 | struct backlight_device *bl_dev; | |
63 | struct gpio_desc *reset_gpio; | |
ded8d7fe | 64 | struct regulator *supply; |
f0a5bb98 PC |
65 | bool prepared; |
66 | bool enabled; | |
67 | }; | |
68 | ||
69 | static const struct drm_display_mode default_mode = { | |
70 | .clock = 32729, | |
71 | .hdisplay = 480, | |
72 | .hsync_start = 480 + 120, | |
73 | .hsync_end = 480 + 120 + 63, | |
74 | .htotal = 480 + 120 + 63 + 120, | |
75 | .vdisplay = 800, | |
76 | .vsync_start = 800 + 12, | |
77 | .vsync_end = 800 + 12 + 12, | |
78 | .vtotal = 800 + 12 + 12 + 12, | |
79 | .vrefresh = 50, | |
80 | .flags = 0, | |
81 | .width_mm = 52, | |
82 | .height_mm = 86, | |
83 | }; | |
84 | ||
85 | static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel) | |
86 | { | |
87 | return container_of(panel, struct otm8009a, panel); | |
88 | } | |
89 | ||
90 | static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data, | |
91 | size_t len) | |
92 | { | |
93 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | |
94 | ||
95 | if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0) | |
96 | DRM_WARN("mipi dsi dcs write buffer failed\n"); | |
97 | } | |
98 | ||
d9f9565c PC |
99 | static void otm8009a_dcs_write_buf_hs(struct otm8009a *ctx, const void *data, |
100 | size_t len) | |
101 | { | |
102 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | |
103 | ||
104 | /* data will be sent in dsi hs mode (ie. no lpm) */ | |
105 | dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; | |
106 | ||
107 | otm8009a_dcs_write_buf(ctx, data, len); | |
108 | ||
109 | /* restore back the dsi lpm mode */ | |
110 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; | |
111 | } | |
112 | ||
f0a5bb98 PC |
113 | #define dcs_write_seq(ctx, seq...) \ |
114 | ({ \ | |
115 | static const u8 d[] = { seq }; \ | |
116 | otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \ | |
117 | }) | |
118 | ||
119 | #define dcs_write_cmd_at(ctx, cmd, seq...) \ | |
120 | ({ \ | |
121 | dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \ | |
122 | dcs_write_seq(ctx, (cmd) >> 8, seq); \ | |
123 | }) | |
124 | ||
125 | static int otm8009a_init_sequence(struct otm8009a *ctx) | |
126 | { | |
127 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | |
128 | int ret; | |
129 | ||
130 | /* Enter CMD2 */ | |
131 | dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01); | |
132 | ||
133 | /* Enter Orise Command2 */ | |
134 | dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09); | |
135 | ||
136 | dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30); | |
137 | mdelay(10); | |
138 | ||
139 | dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40); | |
140 | mdelay(10); | |
141 | ||
142 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9); | |
143 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34); | |
144 | dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50); | |
145 | dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E); | |
146 | dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */ | |
147 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01); | |
148 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34); | |
149 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33); | |
150 | dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79); | |
151 | dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B); | |
152 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83); | |
153 | dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83); | |
154 | dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E); | |
155 | dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01); | |
156 | ||
157 | dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); | |
158 | dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, | |
159 | 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); | |
160 | dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, | |
161 | 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); | |
162 | dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, | |
163 | 0x01, 0x02, 0x00, 0x00); | |
164 | ||
165 | dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00); | |
166 | ||
167 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | |
168 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
169 | 0, 0, 0, 0, 0); | |
170 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
171 | 0, 0, 0, 0, 0); | |
172 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | |
173 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, | |
174 | 0, 0, 0, 0, 0); | |
175 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, | |
176 | 4, 0, 0, 0, 0); | |
177 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | |
178 | dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
179 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); | |
180 | ||
181 | dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, | |
182 | 0x00, 0x00, 0x00, 0x00); | |
183 | dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02); | |
185 | dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, | |
186 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); | |
187 | dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, | |
188 | 0x00, 0x00, 0x00, 0x00); | |
189 | dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01); | |
191 | dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, | |
192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); | |
193 | ||
194 | dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66); | |
195 | ||
196 | dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06); | |
197 | ||
198 | dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, | |
199 | 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, | |
200 | 0x01); | |
201 | dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, | |
202 | 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, | |
203 | 0x01); | |
204 | ||
205 | /* Exit CMD2 */ | |
206 | dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF); | |
207 | ||
208 | ret = mipi_dsi_dcs_nop(dsi); | |
209 | if (ret) | |
210 | return ret; | |
211 | ||
212 | ret = mipi_dsi_dcs_exit_sleep_mode(dsi); | |
213 | if (ret) | |
214 | return ret; | |
215 | ||
216 | /* Wait for sleep out exit */ | |
217 | mdelay(120); | |
218 | ||
219 | /* Default portrait 480x800 rgb24 */ | |
220 | dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); | |
221 | ||
222 | ret = mipi_dsi_dcs_set_column_address(dsi, 0, | |
223 | default_mode.hdisplay - 1); | |
224 | if (ret) | |
225 | return ret; | |
226 | ||
227 | ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); | |
228 | if (ret) | |
229 | return ret; | |
230 | ||
231 | /* See otm8009a driver documentation for pixel format descriptions */ | |
232 | ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT | | |
233 | MIPI_DCS_PIXEL_FMT_24BIT << 4); | |
234 | if (ret) | |
235 | return ret; | |
236 | ||
237 | /* Disable CABC feature */ | |
238 | dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); | |
239 | ||
240 | ret = mipi_dsi_dcs_set_display_on(dsi); | |
241 | if (ret) | |
242 | return ret; | |
243 | ||
244 | ret = mipi_dsi_dcs_nop(dsi); | |
245 | if (ret) | |
246 | return ret; | |
247 | ||
248 | /* Send Command GRAM memory write (no parameters) */ | |
249 | dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START); | |
250 | ||
54ab3624 YF |
251 | /* Wait a short while to let the panel be ready before the 1st frame */ |
252 | mdelay(10); | |
253 | ||
f0a5bb98 PC |
254 | return 0; |
255 | } | |
256 | ||
257 | static int otm8009a_disable(struct drm_panel *panel) | |
258 | { | |
259 | struct otm8009a *ctx = panel_to_otm8009a(panel); | |
260 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | |
261 | int ret; | |
262 | ||
263 | if (!ctx->enabled) | |
264 | return 0; /* This is not an issue so we return 0 here */ | |
265 | ||
12a6cbd4 | 266 | backlight_disable(ctx->bl_dev); |
f0a5bb98 PC |
267 | |
268 | ret = mipi_dsi_dcs_set_display_off(dsi); | |
269 | if (ret) | |
270 | return ret; | |
271 | ||
272 | ret = mipi_dsi_dcs_enter_sleep_mode(dsi); | |
273 | if (ret) | |
274 | return ret; | |
275 | ||
276 | msleep(120); | |
277 | ||
278 | ctx->enabled = false; | |
279 | ||
280 | return 0; | |
281 | } | |
282 | ||
283 | static int otm8009a_unprepare(struct drm_panel *panel) | |
284 | { | |
285 | struct otm8009a *ctx = panel_to_otm8009a(panel); | |
286 | ||
287 | if (!ctx->prepared) | |
288 | return 0; | |
289 | ||
290 | if (ctx->reset_gpio) { | |
291 | gpiod_set_value_cansleep(ctx->reset_gpio, 1); | |
292 | msleep(20); | |
293 | } | |
294 | ||
ded8d7fe PC |
295 | regulator_disable(ctx->supply); |
296 | ||
f0a5bb98 PC |
297 | ctx->prepared = false; |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | static int otm8009a_prepare(struct drm_panel *panel) | |
303 | { | |
304 | struct otm8009a *ctx = panel_to_otm8009a(panel); | |
305 | int ret; | |
306 | ||
307 | if (ctx->prepared) | |
308 | return 0; | |
309 | ||
ded8d7fe PC |
310 | ret = regulator_enable(ctx->supply); |
311 | if (ret < 0) { | |
312 | DRM_ERROR("failed to enable supply: %d\n", ret); | |
313 | return ret; | |
314 | } | |
315 | ||
f0a5bb98 PC |
316 | if (ctx->reset_gpio) { |
317 | gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
318 | gpiod_set_value_cansleep(ctx->reset_gpio, 1); | |
319 | msleep(20); | |
320 | gpiod_set_value_cansleep(ctx->reset_gpio, 0); | |
321 | msleep(100); | |
322 | } | |
323 | ||
324 | ret = otm8009a_init_sequence(ctx); | |
325 | if (ret) | |
326 | return ret; | |
327 | ||
328 | ctx->prepared = true; | |
329 | ||
f0a5bb98 PC |
330 | return 0; |
331 | } | |
332 | ||
333 | static int otm8009a_enable(struct drm_panel *panel) | |
334 | { | |
335 | struct otm8009a *ctx = panel_to_otm8009a(panel); | |
336 | ||
36830ce4 PC |
337 | if (ctx->enabled) |
338 | return 0; | |
339 | ||
12a6cbd4 | 340 | backlight_enable(ctx->bl_dev); |
36830ce4 | 341 | |
f0a5bb98 PC |
342 | ctx->enabled = true; |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | static int otm8009a_get_modes(struct drm_panel *panel) | |
348 | { | |
349 | struct drm_display_mode *mode; | |
350 | ||
351 | mode = drm_mode_duplicate(panel->drm, &default_mode); | |
352 | if (!mode) { | |
353 | DRM_ERROR("failed to add mode %ux%ux@%u\n", | |
354 | default_mode.hdisplay, default_mode.vdisplay, | |
355 | default_mode.vrefresh); | |
356 | return -ENOMEM; | |
357 | } | |
358 | ||
359 | drm_mode_set_name(mode); | |
360 | ||
361 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | |
362 | drm_mode_probed_add(panel->connector, mode); | |
363 | ||
364 | panel->connector->display_info.width_mm = mode->width_mm; | |
365 | panel->connector->display_info.height_mm = mode->height_mm; | |
366 | ||
367 | return 1; | |
368 | } | |
369 | ||
370 | static const struct drm_panel_funcs otm8009a_drm_funcs = { | |
371 | .disable = otm8009a_disable, | |
372 | .unprepare = otm8009a_unprepare, | |
373 | .prepare = otm8009a_prepare, | |
374 | .enable = otm8009a_enable, | |
375 | .get_modes = otm8009a_get_modes, | |
376 | }; | |
377 | ||
378 | /* | |
379 | * DSI-BASED BACKLIGHT | |
380 | */ | |
381 | ||
382 | static int otm8009a_backlight_update_status(struct backlight_device *bd) | |
383 | { | |
384 | struct otm8009a *ctx = bl_get_data(bd); | |
385 | u8 data[2]; | |
386 | ||
387 | if (!ctx->prepared) { | |
388 | DRM_DEBUG("lcd not ready yet for setting its backlight!\n"); | |
389 | return -ENXIO; | |
390 | } | |
391 | ||
392 | if (bd->props.power <= FB_BLANK_NORMAL) { | |
393 | /* Power on the backlight with the requested brightness | |
394 | * Note We can not use mipi_dsi_dcs_set_display_brightness() | |
395 | * as otm8009a driver support only 8-bit brightness (1 param). | |
396 | */ | |
397 | data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS; | |
398 | data[1] = bd->props.brightness; | |
d9f9565c | 399 | otm8009a_dcs_write_buf_hs(ctx, data, ARRAY_SIZE(data)); |
f0a5bb98 PC |
400 | |
401 | /* set Brightness Control & Backlight on */ | |
402 | data[1] = 0x24; | |
403 | ||
404 | } else { | |
405 | /* Power off the backlight: set Brightness Control & Bl off */ | |
406 | data[1] = 0; | |
407 | } | |
408 | ||
409 | /* Update Brightness Control & Backlight */ | |
410 | data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY; | |
d9f9565c | 411 | otm8009a_dcs_write_buf_hs(ctx, data, ARRAY_SIZE(data)); |
f0a5bb98 PC |
412 | |
413 | return 0; | |
414 | } | |
415 | ||
416 | static const struct backlight_ops otm8009a_backlight_ops = { | |
417 | .update_status = otm8009a_backlight_update_status, | |
418 | }; | |
419 | ||
420 | static int otm8009a_probe(struct mipi_dsi_device *dsi) | |
421 | { | |
422 | struct device *dev = &dsi->dev; | |
423 | struct otm8009a *ctx; | |
424 | int ret; | |
425 | ||
426 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | |
427 | if (!ctx) | |
428 | return -ENOMEM; | |
429 | ||
430 | ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); | |
431 | if (IS_ERR(ctx->reset_gpio)) { | |
432 | dev_err(dev, "cannot get reset-gpio\n"); | |
433 | return PTR_ERR(ctx->reset_gpio); | |
434 | } | |
435 | ||
ded8d7fe PC |
436 | ctx->supply = devm_regulator_get(dev, "power"); |
437 | if (IS_ERR(ctx->supply)) { | |
438 | ret = PTR_ERR(ctx->supply); | |
439 | dev_err(dev, "failed to request regulator: %d\n", ret); | |
440 | return ret; | |
441 | } | |
442 | ||
f0a5bb98 PC |
443 | mipi_dsi_set_drvdata(dsi, ctx); |
444 | ||
445 | ctx->dev = dev; | |
446 | ||
447 | dsi->lanes = 2; | |
448 | dsi->format = MIPI_DSI_FMT_RGB888; | |
449 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | | |
450 | MIPI_DSI_MODE_LPM; | |
451 | ||
452 | drm_panel_init(&ctx->panel); | |
453 | ctx->panel.dev = dev; | |
454 | ctx->panel.funcs = &otm8009a_drm_funcs; | |
455 | ||
12a6cbd4 PC |
456 | ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev), |
457 | dsi->host->dev, ctx, | |
458 | &otm8009a_backlight_ops, | |
459 | NULL); | |
f0a5bb98 | 460 | if (IS_ERR(ctx->bl_dev)) { |
12a6cbd4 PC |
461 | ret = PTR_ERR(ctx->bl_dev); |
462 | dev_err(dev, "failed to register backlight: %d\n", ret); | |
463 | return ret; | |
f0a5bb98 PC |
464 | } |
465 | ||
466 | ctx->bl_dev->props.max_brightness = OTM8009A_BACKLIGHT_MAX; | |
467 | ctx->bl_dev->props.brightness = OTM8009A_BACKLIGHT_DEFAULT; | |
468 | ctx->bl_dev->props.power = FB_BLANK_POWERDOWN; | |
469 | ctx->bl_dev->props.type = BACKLIGHT_RAW; | |
470 | ||
471 | drm_panel_add(&ctx->panel); | |
472 | ||
473 | ret = mipi_dsi_attach(dsi); | |
474 | if (ret < 0) { | |
475 | dev_err(dev, "mipi_dsi_attach failed. Is host ready?\n"); | |
476 | drm_panel_remove(&ctx->panel); | |
477 | backlight_device_unregister(ctx->bl_dev); | |
478 | return ret; | |
479 | } | |
480 | ||
f0a5bb98 PC |
481 | return 0; |
482 | } | |
483 | ||
484 | static int otm8009a_remove(struct mipi_dsi_device *dsi) | |
485 | { | |
486 | struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi); | |
487 | ||
488 | mipi_dsi_detach(dsi); | |
489 | drm_panel_remove(&ctx->panel); | |
490 | ||
f0a5bb98 PC |
491 | return 0; |
492 | } | |
493 | ||
494 | static const struct of_device_id orisetech_otm8009a_of_match[] = { | |
495 | { .compatible = "orisetech,otm8009a" }, | |
496 | { } | |
497 | }; | |
498 | MODULE_DEVICE_TABLE(of, orisetech_otm8009a_of_match); | |
499 | ||
500 | static struct mipi_dsi_driver orisetech_otm8009a_driver = { | |
501 | .probe = otm8009a_probe, | |
502 | .remove = otm8009a_remove, | |
503 | .driver = { | |
6982b943 | 504 | .name = "panel-orisetech-otm8009a", |
f0a5bb98 PC |
505 | .of_match_table = orisetech_otm8009a_of_match, |
506 | }, | |
507 | }; | |
508 | module_mipi_dsi_driver(orisetech_otm8009a_driver); | |
509 | ||
510 | MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); | |
511 | MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); | |
512 | MODULE_DESCRIPTION("DRM driver for Orise Tech OTM8009A MIPI DSI panel"); | |
513 | MODULE_LICENSE("GPL v2"); |