1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/backlight.h>
7*4882a593Smuzhiyun #include <linux/delay.h>
8*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/of_device.h>
11*4882a593Smuzhiyun #include <linux/of_graph.h>
12*4882a593Smuzhiyun #include <linux/pinctrl/consumer.h>
13*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <video/mipi_display.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <drm/drm_mipi_dsi.h>
18*4882a593Smuzhiyun #include <drm/drm_modes.h>
19*4882a593Smuzhiyun #include <drm/drm_panel.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun static const char * const regulator_names[] = {
22*4882a593Smuzhiyun "vdda",
23*4882a593Smuzhiyun "vdispp",
24*4882a593Smuzhiyun "vdispn",
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static unsigned long const regulator_enable_loads[] = {
28*4882a593Smuzhiyun 62000,
29*4882a593Smuzhiyun 100000,
30*4882a593Smuzhiyun 100000,
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun static unsigned long const regulator_disable_loads[] = {
34*4882a593Smuzhiyun 80,
35*4882a593Smuzhiyun 100,
36*4882a593Smuzhiyun 100,
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct cmd_set {
40*4882a593Smuzhiyun u8 commands[4];
41*4882a593Smuzhiyun u8 size;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun struct nt35597_config {
45*4882a593Smuzhiyun u32 width_mm;
46*4882a593Smuzhiyun u32 height_mm;
47*4882a593Smuzhiyun const char *panel_name;
48*4882a593Smuzhiyun const struct cmd_set *panel_on_cmds;
49*4882a593Smuzhiyun u32 num_on_cmds;
50*4882a593Smuzhiyun const struct drm_display_mode *dm;
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun struct truly_nt35597 {
54*4882a593Smuzhiyun struct device *dev;
55*4882a593Smuzhiyun struct drm_panel panel;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun struct gpio_desc *reset_gpio;
60*4882a593Smuzhiyun struct gpio_desc *mode_gpio;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun struct backlight_device *backlight;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun struct mipi_dsi_device *dsi[2];
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun const struct nt35597_config *config;
67*4882a593Smuzhiyun bool prepared;
68*4882a593Smuzhiyun bool enabled;
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
panel_to_ctx(struct drm_panel * panel)71*4882a593Smuzhiyun static inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun return container_of(panel, struct truly_nt35597, panel);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun static const struct cmd_set qcom_2k_panel_magic_cmds[] = {
77*4882a593Smuzhiyun /* CMD2_P0 */
78*4882a593Smuzhiyun { { 0xff, 0x20 }, 2 },
79*4882a593Smuzhiyun { { 0xfb, 0x01 }, 2 },
80*4882a593Smuzhiyun { { 0x00, 0x01 }, 2 },
81*4882a593Smuzhiyun { { 0x01, 0x55 }, 2 },
82*4882a593Smuzhiyun { { 0x02, 0x45 }, 2 },
83*4882a593Smuzhiyun { { 0x05, 0x40 }, 2 },
84*4882a593Smuzhiyun { { 0x06, 0x19 }, 2 },
85*4882a593Smuzhiyun { { 0x07, 0x1e }, 2 },
86*4882a593Smuzhiyun { { 0x0b, 0x73 }, 2 },
87*4882a593Smuzhiyun { { 0x0c, 0x73 }, 2 },
88*4882a593Smuzhiyun { { 0x0e, 0xb0 }, 2 },
89*4882a593Smuzhiyun { { 0x0f, 0xae }, 2 },
90*4882a593Smuzhiyun { { 0x11, 0xb8 }, 2 },
91*4882a593Smuzhiyun { { 0x13, 0x00 }, 2 },
92*4882a593Smuzhiyun { { 0x58, 0x80 }, 2 },
93*4882a593Smuzhiyun { { 0x59, 0x01 }, 2 },
94*4882a593Smuzhiyun { { 0x5a, 0x00 }, 2 },
95*4882a593Smuzhiyun { { 0x5b, 0x01 }, 2 },
96*4882a593Smuzhiyun { { 0x5c, 0x80 }, 2 },
97*4882a593Smuzhiyun { { 0x5d, 0x81 }, 2 },
98*4882a593Smuzhiyun { { 0x5e, 0x00 }, 2 },
99*4882a593Smuzhiyun { { 0x5f, 0x01 }, 2 },
100*4882a593Smuzhiyun { { 0x72, 0x11 }, 2 },
101*4882a593Smuzhiyun { { 0x68, 0x03 }, 2 },
102*4882a593Smuzhiyun /* CMD2_P4 */
103*4882a593Smuzhiyun { { 0xFF, 0x24 }, 2 },
104*4882a593Smuzhiyun { { 0xFB, 0x01 }, 2 },
105*4882a593Smuzhiyun { { 0x00, 0x1C }, 2 },
106*4882a593Smuzhiyun { { 0x01, 0x0B }, 2 },
107*4882a593Smuzhiyun { { 0x02, 0x0C }, 2 },
108*4882a593Smuzhiyun { { 0x03, 0x01 }, 2 },
109*4882a593Smuzhiyun { { 0x04, 0x0F }, 2 },
110*4882a593Smuzhiyun { { 0x05, 0x10 }, 2 },
111*4882a593Smuzhiyun { { 0x06, 0x10 }, 2 },
112*4882a593Smuzhiyun { { 0x07, 0x10 }, 2 },
113*4882a593Smuzhiyun { { 0x08, 0x89 }, 2 },
114*4882a593Smuzhiyun { { 0x09, 0x8A }, 2 },
115*4882a593Smuzhiyun { { 0x0A, 0x13 }, 2 },
116*4882a593Smuzhiyun { { 0x0B, 0x13 }, 2 },
117*4882a593Smuzhiyun { { 0x0C, 0x15 }, 2 },
118*4882a593Smuzhiyun { { 0x0D, 0x15 }, 2 },
119*4882a593Smuzhiyun { { 0x0E, 0x17 }, 2 },
120*4882a593Smuzhiyun { { 0x0F, 0x17 }, 2 },
121*4882a593Smuzhiyun { { 0x10, 0x1C }, 2 },
122*4882a593Smuzhiyun { { 0x11, 0x0B }, 2 },
123*4882a593Smuzhiyun { { 0x12, 0x0C }, 2 },
124*4882a593Smuzhiyun { { 0x13, 0x01 }, 2 },
125*4882a593Smuzhiyun { { 0x14, 0x0F }, 2 },
126*4882a593Smuzhiyun { { 0x15, 0x10 }, 2 },
127*4882a593Smuzhiyun { { 0x16, 0x10 }, 2 },
128*4882a593Smuzhiyun { { 0x17, 0x10 }, 2 },
129*4882a593Smuzhiyun { { 0x18, 0x89 }, 2 },
130*4882a593Smuzhiyun { { 0x19, 0x8A }, 2 },
131*4882a593Smuzhiyun { { 0x1A, 0x13 }, 2 },
132*4882a593Smuzhiyun { { 0x1B, 0x13 }, 2 },
133*4882a593Smuzhiyun { { 0x1C, 0x15 }, 2 },
134*4882a593Smuzhiyun { { 0x1D, 0x15 }, 2 },
135*4882a593Smuzhiyun { { 0x1E, 0x17 }, 2 },
136*4882a593Smuzhiyun { { 0x1F, 0x17 }, 2 },
137*4882a593Smuzhiyun /* STV */
138*4882a593Smuzhiyun { { 0x20, 0x40 }, 2 },
139*4882a593Smuzhiyun { { 0x21, 0x01 }, 2 },
140*4882a593Smuzhiyun { { 0x22, 0x00 }, 2 },
141*4882a593Smuzhiyun { { 0x23, 0x40 }, 2 },
142*4882a593Smuzhiyun { { 0x24, 0x40 }, 2 },
143*4882a593Smuzhiyun { { 0x25, 0x6D }, 2 },
144*4882a593Smuzhiyun { { 0x26, 0x40 }, 2 },
145*4882a593Smuzhiyun { { 0x27, 0x40 }, 2 },
146*4882a593Smuzhiyun /* Vend */
147*4882a593Smuzhiyun { { 0xE0, 0x00 }, 2 },
148*4882a593Smuzhiyun { { 0xDC, 0x21 }, 2 },
149*4882a593Smuzhiyun { { 0xDD, 0x22 }, 2 },
150*4882a593Smuzhiyun { { 0xDE, 0x07 }, 2 },
151*4882a593Smuzhiyun { { 0xDF, 0x07 }, 2 },
152*4882a593Smuzhiyun { { 0xE3, 0x6D }, 2 },
153*4882a593Smuzhiyun { { 0xE1, 0x07 }, 2 },
154*4882a593Smuzhiyun { { 0xE2, 0x07 }, 2 },
155*4882a593Smuzhiyun /* UD */
156*4882a593Smuzhiyun { { 0x29, 0xD8 }, 2 },
157*4882a593Smuzhiyun { { 0x2A, 0x2A }, 2 },
158*4882a593Smuzhiyun /* CLK */
159*4882a593Smuzhiyun { { 0x4B, 0x03 }, 2 },
160*4882a593Smuzhiyun { { 0x4C, 0x11 }, 2 },
161*4882a593Smuzhiyun { { 0x4D, 0x10 }, 2 },
162*4882a593Smuzhiyun { { 0x4E, 0x01 }, 2 },
163*4882a593Smuzhiyun { { 0x4F, 0x01 }, 2 },
164*4882a593Smuzhiyun { { 0x50, 0x10 }, 2 },
165*4882a593Smuzhiyun { { 0x51, 0x00 }, 2 },
166*4882a593Smuzhiyun { { 0x52, 0x80 }, 2 },
167*4882a593Smuzhiyun { { 0x53, 0x00 }, 2 },
168*4882a593Smuzhiyun { { 0x56, 0x00 }, 2 },
169*4882a593Smuzhiyun { { 0x54, 0x07 }, 2 },
170*4882a593Smuzhiyun { { 0x58, 0x07 }, 2 },
171*4882a593Smuzhiyun { { 0x55, 0x25 }, 2 },
172*4882a593Smuzhiyun /* Reset XDONB */
173*4882a593Smuzhiyun { { 0x5B, 0x43 }, 2 },
174*4882a593Smuzhiyun { { 0x5C, 0x00 }, 2 },
175*4882a593Smuzhiyun { { 0x5F, 0x73 }, 2 },
176*4882a593Smuzhiyun { { 0x60, 0x73 }, 2 },
177*4882a593Smuzhiyun { { 0x63, 0x22 }, 2 },
178*4882a593Smuzhiyun { { 0x64, 0x00 }, 2 },
179*4882a593Smuzhiyun { { 0x67, 0x08 }, 2 },
180*4882a593Smuzhiyun { { 0x68, 0x04 }, 2 },
181*4882a593Smuzhiyun /* Resolution:1440x2560 */
182*4882a593Smuzhiyun { { 0x72, 0x02 }, 2 },
183*4882a593Smuzhiyun /* mux */
184*4882a593Smuzhiyun { { 0x7A, 0x80 }, 2 },
185*4882a593Smuzhiyun { { 0x7B, 0x91 }, 2 },
186*4882a593Smuzhiyun { { 0x7C, 0xD8 }, 2 },
187*4882a593Smuzhiyun { { 0x7D, 0x60 }, 2 },
188*4882a593Smuzhiyun { { 0x7F, 0x15 }, 2 },
189*4882a593Smuzhiyun { { 0x75, 0x15 }, 2 },
190*4882a593Smuzhiyun /* ABOFF */
191*4882a593Smuzhiyun { { 0xB3, 0xC0 }, 2 },
192*4882a593Smuzhiyun { { 0xB4, 0x00 }, 2 },
193*4882a593Smuzhiyun { { 0xB5, 0x00 }, 2 },
194*4882a593Smuzhiyun /* Source EQ */
195*4882a593Smuzhiyun { { 0x78, 0x00 }, 2 },
196*4882a593Smuzhiyun { { 0x79, 0x00 }, 2 },
197*4882a593Smuzhiyun { { 0x80, 0x00 }, 2 },
198*4882a593Smuzhiyun { { 0x83, 0x00 }, 2 },
199*4882a593Smuzhiyun /* FP BP */
200*4882a593Smuzhiyun { { 0x93, 0x0A }, 2 },
201*4882a593Smuzhiyun { { 0x94, 0x0A }, 2 },
202*4882a593Smuzhiyun /* Inversion Type */
203*4882a593Smuzhiyun { { 0x8A, 0x00 }, 2 },
204*4882a593Smuzhiyun { { 0x9B, 0xFF }, 2 },
205*4882a593Smuzhiyun /* IMGSWAP =1 @PortSwap=1 */
206*4882a593Smuzhiyun { { 0x9D, 0xB0 }, 2 },
207*4882a593Smuzhiyun { { 0x9F, 0x63 }, 2 },
208*4882a593Smuzhiyun { { 0x98, 0x10 }, 2 },
209*4882a593Smuzhiyun /* FRM */
210*4882a593Smuzhiyun { { 0xEC, 0x00 }, 2 },
211*4882a593Smuzhiyun /* CMD1 */
212*4882a593Smuzhiyun { { 0xFF, 0x10 }, 2 },
213*4882a593Smuzhiyun /* VBP+VSA=,VFP = 10H */
214*4882a593Smuzhiyun { { 0x3B, 0x03, 0x0A, 0x0A }, 4 },
215*4882a593Smuzhiyun /* FTE on */
216*4882a593Smuzhiyun { { 0x35, 0x00 }, 2 },
217*4882a593Smuzhiyun /* EN_BK =1(auto black) */
218*4882a593Smuzhiyun { { 0xE5, 0x01 }, 2 },
219*4882a593Smuzhiyun /* CMD mode(10) VDO mode(03) */
220*4882a593Smuzhiyun { { 0xBB, 0x03 }, 2 },
221*4882a593Smuzhiyun /* Non Reload MTP */
222*4882a593Smuzhiyun { { 0xFB, 0x01 }, 2 },
223*4882a593Smuzhiyun };
224*4882a593Smuzhiyun
truly_dcs_write(struct drm_panel * panel,u32 command)225*4882a593Smuzhiyun static int truly_dcs_write(struct drm_panel *panel, u32 command)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
228*4882a593Smuzhiyun int i, ret;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
231*4882a593Smuzhiyun ret = mipi_dsi_dcs_write(ctx->dsi[i], command, NULL, 0);
232*4882a593Smuzhiyun if (ret < 0) {
233*4882a593Smuzhiyun dev_err(ctx->dev, "cmd 0x%x failed for dsi = %d\n", command, i);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return ret;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
truly_dcs_write_buf(struct drm_panel * panel,u32 size,const u8 * buf)240*4882a593Smuzhiyun static int truly_dcs_write_buf(struct drm_panel *panel,
241*4882a593Smuzhiyun u32 size, const u8 *buf)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
244*4882a593Smuzhiyun int ret = 0;
245*4882a593Smuzhiyun int i;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
248*4882a593Smuzhiyun ret = mipi_dsi_dcs_write_buffer(ctx->dsi[i], buf, size);
249*4882a593Smuzhiyun if (ret < 0) {
250*4882a593Smuzhiyun dev_err(ctx->dev, "failed to tx cmd [%d], err: %d\n", i, ret);
251*4882a593Smuzhiyun return ret;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun return ret;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
truly_35597_power_on(struct truly_nt35597 * ctx)258*4882a593Smuzhiyun static int truly_35597_power_on(struct truly_nt35597 *ctx)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun int ret, i;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
263*4882a593Smuzhiyun ret = regulator_set_load(ctx->supplies[i].consumer,
264*4882a593Smuzhiyun regulator_enable_loads[i]);
265*4882a593Smuzhiyun if (ret)
266*4882a593Smuzhiyun return ret;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
270*4882a593Smuzhiyun if (ret < 0)
271*4882a593Smuzhiyun return ret;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /*
274*4882a593Smuzhiyun * Reset sequence of truly panel requires the panel to be
275*4882a593Smuzhiyun * out of reset for 10ms, followed by being held in reset
276*4882a593Smuzhiyun * for 10ms and then out again
277*4882a593Smuzhiyun */
278*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 0);
279*4882a593Smuzhiyun usleep_range(10000, 20000);
280*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 1);
281*4882a593Smuzhiyun usleep_range(10000, 20000);
282*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 0);
283*4882a593Smuzhiyun usleep_range(10000, 20000);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun return 0;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
truly_nt35597_power_off(struct truly_nt35597 * ctx)288*4882a593Smuzhiyun static int truly_nt35597_power_off(struct truly_nt35597 *ctx)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun int ret = 0;
291*4882a593Smuzhiyun int i;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 1);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {
296*4882a593Smuzhiyun ret = regulator_set_load(ctx->supplies[i].consumer,
297*4882a593Smuzhiyun regulator_disable_loads[i]);
298*4882a593Smuzhiyun if (ret) {
299*4882a593Smuzhiyun dev_err(ctx->dev, "regulator_set_load failed %d\n", ret);
300*4882a593Smuzhiyun return ret;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
305*4882a593Smuzhiyun if (ret) {
306*4882a593Smuzhiyun dev_err(ctx->dev, "regulator_bulk_disable failed %d\n", ret);
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun return ret;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
truly_nt35597_disable(struct drm_panel * panel)311*4882a593Smuzhiyun static int truly_nt35597_disable(struct drm_panel *panel)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
314*4882a593Smuzhiyun int ret;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun if (!ctx->enabled)
317*4882a593Smuzhiyun return 0;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun if (ctx->backlight) {
320*4882a593Smuzhiyun ret = backlight_disable(ctx->backlight);
321*4882a593Smuzhiyun if (ret < 0)
322*4882a593Smuzhiyun dev_err(ctx->dev, "backlight disable failed %d\n", ret);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun ctx->enabled = false;
326*4882a593Smuzhiyun return 0;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
truly_nt35597_unprepare(struct drm_panel * panel)329*4882a593Smuzhiyun static int truly_nt35597_unprepare(struct drm_panel *panel)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
332*4882a593Smuzhiyun int ret = 0;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun if (!ctx->prepared)
335*4882a593Smuzhiyun return 0;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun ctx->dsi[0]->mode_flags = 0;
338*4882a593Smuzhiyun ctx->dsi[1]->mode_flags = 0;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_OFF);
341*4882a593Smuzhiyun if (ret < 0) {
342*4882a593Smuzhiyun dev_err(ctx->dev, "set_display_off cmd failed ret = %d\n", ret);
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun /* 120ms delay required here as per DCS spec */
346*4882a593Smuzhiyun msleep(120);
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun ret = truly_dcs_write(panel, MIPI_DCS_ENTER_SLEEP_MODE);
349*4882a593Smuzhiyun if (ret < 0) {
350*4882a593Smuzhiyun dev_err(ctx->dev, "enter_sleep cmd failed ret = %d\n", ret);
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun ret = truly_nt35597_power_off(ctx);
354*4882a593Smuzhiyun if (ret < 0)
355*4882a593Smuzhiyun dev_err(ctx->dev, "power_off failed ret = %d\n", ret);
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun ctx->prepared = false;
358*4882a593Smuzhiyun return ret;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
truly_nt35597_prepare(struct drm_panel * panel)361*4882a593Smuzhiyun static int truly_nt35597_prepare(struct drm_panel *panel)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
364*4882a593Smuzhiyun int ret;
365*4882a593Smuzhiyun int i;
366*4882a593Smuzhiyun const struct cmd_set *panel_on_cmds;
367*4882a593Smuzhiyun const struct nt35597_config *config;
368*4882a593Smuzhiyun u32 num_cmds;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun if (ctx->prepared)
371*4882a593Smuzhiyun return 0;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun ret = truly_35597_power_on(ctx);
374*4882a593Smuzhiyun if (ret < 0)
375*4882a593Smuzhiyun return ret;
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
378*4882a593Smuzhiyun ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun config = ctx->config;
381*4882a593Smuzhiyun panel_on_cmds = config->panel_on_cmds;
382*4882a593Smuzhiyun num_cmds = config->num_on_cmds;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun for (i = 0; i < num_cmds; i++) {
385*4882a593Smuzhiyun ret = truly_dcs_write_buf(panel,
386*4882a593Smuzhiyun panel_on_cmds[i].size,
387*4882a593Smuzhiyun panel_on_cmds[i].commands);
388*4882a593Smuzhiyun if (ret < 0) {
389*4882a593Smuzhiyun dev_err(ctx->dev, "cmd set tx failed i = %d ret = %d\n", i, ret);
390*4882a593Smuzhiyun goto power_off;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun ret = truly_dcs_write(panel, MIPI_DCS_EXIT_SLEEP_MODE);
395*4882a593Smuzhiyun if (ret < 0) {
396*4882a593Smuzhiyun dev_err(ctx->dev, "exit_sleep_mode cmd failed ret = %d\n", ret);
397*4882a593Smuzhiyun goto power_off;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun /* Per DSI spec wait 120ms after sending exit sleep DCS command */
401*4882a593Smuzhiyun msleep(120);
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_ON);
404*4882a593Smuzhiyun if (ret < 0) {
405*4882a593Smuzhiyun dev_err(ctx->dev, "set_display_on cmd failed ret = %d\n", ret);
406*4882a593Smuzhiyun goto power_off;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun /* Per DSI spec wait 120ms after sending set_display_on DCS command */
410*4882a593Smuzhiyun msleep(120);
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun ctx->prepared = true;
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun return 0;
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun power_off:
417*4882a593Smuzhiyun if (truly_nt35597_power_off(ctx))
418*4882a593Smuzhiyun dev_err(ctx->dev, "power_off failed\n");
419*4882a593Smuzhiyun return ret;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
truly_nt35597_enable(struct drm_panel * panel)422*4882a593Smuzhiyun static int truly_nt35597_enable(struct drm_panel *panel)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
425*4882a593Smuzhiyun int ret;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun if (ctx->enabled)
428*4882a593Smuzhiyun return 0;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun if (ctx->backlight) {
431*4882a593Smuzhiyun ret = backlight_enable(ctx->backlight);
432*4882a593Smuzhiyun if (ret < 0)
433*4882a593Smuzhiyun dev_err(ctx->dev, "backlight enable failed %d\n", ret);
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun ctx->enabled = true;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun return 0;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun
truly_nt35597_get_modes(struct drm_panel * panel,struct drm_connector * connector)441*4882a593Smuzhiyun static int truly_nt35597_get_modes(struct drm_panel *panel,
442*4882a593Smuzhiyun struct drm_connector *connector)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun struct truly_nt35597 *ctx = panel_to_ctx(panel);
445*4882a593Smuzhiyun struct drm_display_mode *mode;
446*4882a593Smuzhiyun const struct nt35597_config *config;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun config = ctx->config;
449*4882a593Smuzhiyun mode = drm_mode_create(connector->dev);
450*4882a593Smuzhiyun if (!mode) {
451*4882a593Smuzhiyun dev_err(ctx->dev, "failed to create a new display mode\n");
452*4882a593Smuzhiyun return 0;
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun connector->display_info.width_mm = config->width_mm;
456*4882a593Smuzhiyun connector->display_info.height_mm = config->height_mm;
457*4882a593Smuzhiyun drm_mode_copy(mode, config->dm);
458*4882a593Smuzhiyun mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
459*4882a593Smuzhiyun drm_mode_probed_add(connector, mode);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun return 1;
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun static const struct drm_panel_funcs truly_nt35597_drm_funcs = {
465*4882a593Smuzhiyun .disable = truly_nt35597_disable,
466*4882a593Smuzhiyun .unprepare = truly_nt35597_unprepare,
467*4882a593Smuzhiyun .prepare = truly_nt35597_prepare,
468*4882a593Smuzhiyun .enable = truly_nt35597_enable,
469*4882a593Smuzhiyun .get_modes = truly_nt35597_get_modes,
470*4882a593Smuzhiyun };
471*4882a593Smuzhiyun
truly_nt35597_panel_add(struct truly_nt35597 * ctx)472*4882a593Smuzhiyun static int truly_nt35597_panel_add(struct truly_nt35597 *ctx)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun struct device *dev = ctx->dev;
475*4882a593Smuzhiyun int ret, i;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
478*4882a593Smuzhiyun ctx->supplies[i].supply = regulator_names[i];
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
481*4882a593Smuzhiyun ctx->supplies);
482*4882a593Smuzhiyun if (ret < 0)
483*4882a593Smuzhiyun return ret;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
486*4882a593Smuzhiyun if (IS_ERR(ctx->reset_gpio)) {
487*4882a593Smuzhiyun dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio));
488*4882a593Smuzhiyun return PTR_ERR(ctx->reset_gpio);
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun ctx->mode_gpio = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);
492*4882a593Smuzhiyun if (IS_ERR(ctx->mode_gpio)) {
493*4882a593Smuzhiyun dev_err(dev, "cannot get mode gpio %ld\n", PTR_ERR(ctx->mode_gpio));
494*4882a593Smuzhiyun return PTR_ERR(ctx->mode_gpio);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun /* dual port */
498*4882a593Smuzhiyun gpiod_set_value(ctx->mode_gpio, 0);
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun drm_panel_init(&ctx->panel, dev, &truly_nt35597_drm_funcs,
501*4882a593Smuzhiyun DRM_MODE_CONNECTOR_DSI);
502*4882a593Smuzhiyun drm_panel_add(&ctx->panel);
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun return 0;
505*4882a593Smuzhiyun }
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun static const struct drm_display_mode qcom_sdm845_mtp_2k_mode = {
508*4882a593Smuzhiyun .name = "1440x2560",
509*4882a593Smuzhiyun .clock = 268316,
510*4882a593Smuzhiyun .hdisplay = 1440,
511*4882a593Smuzhiyun .hsync_start = 1440 + 200,
512*4882a593Smuzhiyun .hsync_end = 1440 + 200 + 32,
513*4882a593Smuzhiyun .htotal = 1440 + 200 + 32 + 64,
514*4882a593Smuzhiyun .vdisplay = 2560,
515*4882a593Smuzhiyun .vsync_start = 2560 + 8,
516*4882a593Smuzhiyun .vsync_end = 2560 + 8 + 1,
517*4882a593Smuzhiyun .vtotal = 2560 + 8 + 1 + 7,
518*4882a593Smuzhiyun .flags = 0,
519*4882a593Smuzhiyun };
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun static const struct nt35597_config nt35597_dir = {
522*4882a593Smuzhiyun .width_mm = 74,
523*4882a593Smuzhiyun .height_mm = 131,
524*4882a593Smuzhiyun .panel_name = "qcom_sdm845_mtp_2k_panel",
525*4882a593Smuzhiyun .dm = &qcom_sdm845_mtp_2k_mode,
526*4882a593Smuzhiyun .panel_on_cmds = qcom_2k_panel_magic_cmds,
527*4882a593Smuzhiyun .num_on_cmds = ARRAY_SIZE(qcom_2k_panel_magic_cmds),
528*4882a593Smuzhiyun };
529*4882a593Smuzhiyun
truly_nt35597_probe(struct mipi_dsi_device * dsi)530*4882a593Smuzhiyun static int truly_nt35597_probe(struct mipi_dsi_device *dsi)
531*4882a593Smuzhiyun {
532*4882a593Smuzhiyun struct device *dev = &dsi->dev;
533*4882a593Smuzhiyun struct truly_nt35597 *ctx;
534*4882a593Smuzhiyun struct mipi_dsi_device *dsi1_device;
535*4882a593Smuzhiyun struct device_node *dsi1;
536*4882a593Smuzhiyun struct mipi_dsi_host *dsi1_host;
537*4882a593Smuzhiyun struct mipi_dsi_device *dsi_dev;
538*4882a593Smuzhiyun int ret = 0;
539*4882a593Smuzhiyun int i;
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun const struct mipi_dsi_device_info info = {
542*4882a593Smuzhiyun .type = "trulynt35597",
543*4882a593Smuzhiyun .channel = 0,
544*4882a593Smuzhiyun .node = NULL,
545*4882a593Smuzhiyun };
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun if (!ctx)
550*4882a593Smuzhiyun return -ENOMEM;
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun /*
553*4882a593Smuzhiyun * This device represents itself as one with two input ports which are
554*4882a593Smuzhiyun * fed by the output ports of the two DSI controllers . The DSI0 is
555*4882a593Smuzhiyun * the master controller and has most of the panel related info in its
556*4882a593Smuzhiyun * child node.
557*4882a593Smuzhiyun */
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun ctx->config = of_device_get_match_data(dev);
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun if (!ctx->config) {
562*4882a593Smuzhiyun dev_err(dev, "missing device configuration\n");
563*4882a593Smuzhiyun return -ENODEV;
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
567*4882a593Smuzhiyun if (!dsi1) {
568*4882a593Smuzhiyun dev_err(dev, "failed to get remote node for dsi1_device\n");
569*4882a593Smuzhiyun return -ENODEV;
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
573*4882a593Smuzhiyun of_node_put(dsi1);
574*4882a593Smuzhiyun if (!dsi1_host) {
575*4882a593Smuzhiyun dev_err(dev, "failed to find dsi host\n");
576*4882a593Smuzhiyun return -EPROBE_DEFER;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun /* register the second DSI device */
580*4882a593Smuzhiyun dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info);
581*4882a593Smuzhiyun if (IS_ERR(dsi1_device)) {
582*4882a593Smuzhiyun dev_err(dev, "failed to create dsi device\n");
583*4882a593Smuzhiyun return PTR_ERR(dsi1_device);
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun mipi_dsi_set_drvdata(dsi, ctx);
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun ctx->dev = dev;
589*4882a593Smuzhiyun ctx->dsi[0] = dsi;
590*4882a593Smuzhiyun ctx->dsi[1] = dsi1_device;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun ret = truly_nt35597_panel_add(ctx);
593*4882a593Smuzhiyun if (ret) {
594*4882a593Smuzhiyun dev_err(dev, "failed to add panel\n");
595*4882a593Smuzhiyun goto err_panel_add;
596*4882a593Smuzhiyun }
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
599*4882a593Smuzhiyun dsi_dev = ctx->dsi[i];
600*4882a593Smuzhiyun dsi_dev->lanes = 4;
601*4882a593Smuzhiyun dsi_dev->format = MIPI_DSI_FMT_RGB888;
602*4882a593Smuzhiyun dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |
603*4882a593Smuzhiyun MIPI_DSI_CLOCK_NON_CONTINUOUS;
604*4882a593Smuzhiyun ret = mipi_dsi_attach(dsi_dev);
605*4882a593Smuzhiyun if (ret < 0) {
606*4882a593Smuzhiyun dev_err(dev, "dsi attach failed i = %d\n", i);
607*4882a593Smuzhiyun goto err_dsi_attach;
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun return 0;
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun err_dsi_attach:
614*4882a593Smuzhiyun drm_panel_remove(&ctx->panel);
615*4882a593Smuzhiyun err_panel_add:
616*4882a593Smuzhiyun mipi_dsi_device_unregister(dsi1_device);
617*4882a593Smuzhiyun return ret;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun
truly_nt35597_remove(struct mipi_dsi_device * dsi)620*4882a593Smuzhiyun static int truly_nt35597_remove(struct mipi_dsi_device *dsi)
621*4882a593Smuzhiyun {
622*4882a593Smuzhiyun struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi);
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun if (ctx->dsi[0])
625*4882a593Smuzhiyun mipi_dsi_detach(ctx->dsi[0]);
626*4882a593Smuzhiyun if (ctx->dsi[1]) {
627*4882a593Smuzhiyun mipi_dsi_detach(ctx->dsi[1]);
628*4882a593Smuzhiyun mipi_dsi_device_unregister(ctx->dsi[1]);
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun drm_panel_remove(&ctx->panel);
632*4882a593Smuzhiyun return 0;
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun static const struct of_device_id truly_nt35597_of_match[] = {
636*4882a593Smuzhiyun {
637*4882a593Smuzhiyun .compatible = "truly,nt35597-2K-display",
638*4882a593Smuzhiyun .data = &nt35597_dir,
639*4882a593Smuzhiyun },
640*4882a593Smuzhiyun { }
641*4882a593Smuzhiyun };
642*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, truly_nt35597_of_match);
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun static struct mipi_dsi_driver truly_nt35597_driver = {
645*4882a593Smuzhiyun .driver = {
646*4882a593Smuzhiyun .name = "panel-truly-nt35597",
647*4882a593Smuzhiyun .of_match_table = truly_nt35597_of_match,
648*4882a593Smuzhiyun },
649*4882a593Smuzhiyun .probe = truly_nt35597_probe,
650*4882a593Smuzhiyun .remove = truly_nt35597_remove,
651*4882a593Smuzhiyun };
652*4882a593Smuzhiyun module_mipi_dsi_driver(truly_nt35597_driver);
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun MODULE_DESCRIPTION("Truly NT35597 DSI Panel Driver");
655*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
656