1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * MIPI-DSI based S6E63J0X03 AMOLED lcd 1.63 inch panel driver.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Inki Dae <inki.dae@samsung.com>
8*4882a593Smuzhiyun * Hoegeun Kwon <hoegeun.kwon@samsung.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/backlight.h>
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <video/mipi_display.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <drm/drm_mipi_dsi.h>
20*4882a593Smuzhiyun #include <drm/drm_modes.h>
21*4882a593Smuzhiyun #include <drm/drm_panel.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #define MCS_LEVEL2_KEY 0xf0
24*4882a593Smuzhiyun #define MCS_MTP_KEY 0xf1
25*4882a593Smuzhiyun #define MCS_MTP_SET3 0xd4
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define MAX_BRIGHTNESS 100
28*4882a593Smuzhiyun #define DEFAULT_BRIGHTNESS 80
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define NUM_GAMMA_STEPS 9
31*4882a593Smuzhiyun #define GAMMA_CMD_CNT 28
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #define FIRST_COLUMN 20
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun struct s6e63j0x03 {
36*4882a593Smuzhiyun struct device *dev;
37*4882a593Smuzhiyun struct drm_panel panel;
38*4882a593Smuzhiyun struct backlight_device *bl_dev;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun struct regulator_bulk_data supplies[2];
41*4882a593Smuzhiyun struct gpio_desc *reset_gpio;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun static const struct drm_display_mode default_mode = {
45*4882a593Smuzhiyun .clock = 4649,
46*4882a593Smuzhiyun .hdisplay = 320,
47*4882a593Smuzhiyun .hsync_start = 320 + 1,
48*4882a593Smuzhiyun .hsync_end = 320 + 1 + 1,
49*4882a593Smuzhiyun .htotal = 320 + 1 + 1 + 1,
50*4882a593Smuzhiyun .vdisplay = 320,
51*4882a593Smuzhiyun .vsync_start = 320 + 150,
52*4882a593Smuzhiyun .vsync_end = 320 + 150 + 1,
53*4882a593Smuzhiyun .vtotal = 320 + 150 + 1 + 2,
54*4882a593Smuzhiyun .flags = 0,
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static const unsigned char gamma_tbl[NUM_GAMMA_STEPS][GAMMA_CMD_CNT] = {
58*4882a593Smuzhiyun { /* Gamma 10 */
59*4882a593Smuzhiyun MCS_MTP_SET3,
60*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x52, 0x6b, 0x6f, 0x26,
61*4882a593Smuzhiyun 0x28, 0x2d, 0x28, 0x26, 0x27, 0x33, 0x34, 0x32, 0x36, 0x36,
62*4882a593Smuzhiyun 0x35, 0x00, 0xab, 0x00, 0xae, 0x00, 0xbf
63*4882a593Smuzhiyun },
64*4882a593Smuzhiyun { /* gamma 30 */
65*4882a593Smuzhiyun MCS_MTP_SET3,
66*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x70, 0x7f, 0x7f, 0x4e, 0x64, 0x69, 0x26,
67*4882a593Smuzhiyun 0x27, 0x2a, 0x28, 0x29, 0x27, 0x31, 0x32, 0x31, 0x35, 0x34,
68*4882a593Smuzhiyun 0x35, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xdc
69*4882a593Smuzhiyun },
70*4882a593Smuzhiyun { /* gamma 60 */
71*4882a593Smuzhiyun MCS_MTP_SET3,
72*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x65, 0x7b, 0x7d, 0x5f, 0x67, 0x68, 0x2a,
73*4882a593Smuzhiyun 0x28, 0x29, 0x28, 0x2a, 0x27, 0x31, 0x2f, 0x30, 0x34, 0x33,
74*4882a593Smuzhiyun 0x34, 0x00, 0xd9, 0x00, 0xe4, 0x00, 0xf5
75*4882a593Smuzhiyun },
76*4882a593Smuzhiyun { /* gamma 90 */
77*4882a593Smuzhiyun MCS_MTP_SET3,
78*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x4d, 0x6f, 0x71, 0x67, 0x6a, 0x6c, 0x29,
79*4882a593Smuzhiyun 0x28, 0x28, 0x28, 0x29, 0x27, 0x30, 0x2e, 0x30, 0x32, 0x31,
80*4882a593Smuzhiyun 0x31, 0x00, 0xea, 0x00, 0xf6, 0x01, 0x09
81*4882a593Smuzhiyun },
82*4882a593Smuzhiyun { /* gamma 120 */
83*4882a593Smuzhiyun MCS_MTP_SET3,
84*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x3d, 0x66, 0x68, 0x69, 0x69, 0x69, 0x28,
85*4882a593Smuzhiyun 0x28, 0x27, 0x28, 0x28, 0x27, 0x30, 0x2e, 0x2f, 0x31, 0x31,
86*4882a593Smuzhiyun 0x30, 0x00, 0xf9, 0x01, 0x05, 0x01, 0x1b
87*4882a593Smuzhiyun },
88*4882a593Smuzhiyun { /* gamma 150 */
89*4882a593Smuzhiyun MCS_MTP_SET3,
90*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x31, 0x51, 0x53, 0x66, 0x66, 0x67, 0x28,
91*4882a593Smuzhiyun 0x29, 0x27, 0x28, 0x27, 0x27, 0x2e, 0x2d, 0x2e, 0x31, 0x31,
92*4882a593Smuzhiyun 0x30, 0x01, 0x04, 0x01, 0x11, 0x01, 0x29
93*4882a593Smuzhiyun },
94*4882a593Smuzhiyun { /* gamma 200 */
95*4882a593Smuzhiyun MCS_MTP_SET3,
96*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x51, 0x67, 0x65, 0x65, 0x29,
97*4882a593Smuzhiyun 0x2a, 0x28, 0x27, 0x25, 0x26, 0x2d, 0x2c, 0x2c, 0x30, 0x30,
98*4882a593Smuzhiyun 0x30, 0x01, 0x14, 0x01, 0x23, 0x01, 0x3b
99*4882a593Smuzhiyun },
100*4882a593Smuzhiyun { /* gamma 240 */
101*4882a593Smuzhiyun MCS_MTP_SET3,
102*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x2c, 0x4d, 0x50, 0x65, 0x63, 0x64, 0x2a,
103*4882a593Smuzhiyun 0x2c, 0x29, 0x26, 0x24, 0x25, 0x2c, 0x2b, 0x2b, 0x30, 0x30,
104*4882a593Smuzhiyun 0x30, 0x01, 0x1e, 0x01, 0x2f, 0x01, 0x47
105*4882a593Smuzhiyun },
106*4882a593Smuzhiyun { /* gamma 300 */
107*4882a593Smuzhiyun MCS_MTP_SET3,
108*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x38, 0x61, 0x64, 0x65, 0x63, 0x64, 0x28,
109*4882a593Smuzhiyun 0x2a, 0x27, 0x26, 0x23, 0x25, 0x2b, 0x2b, 0x2a, 0x30, 0x2f,
110*4882a593Smuzhiyun 0x30, 0x01, 0x2d, 0x01, 0x3f, 0x01, 0x57
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun };
113*4882a593Smuzhiyun
panel_to_s6e63j0x03(struct drm_panel * panel)114*4882a593Smuzhiyun static inline struct s6e63j0x03 *panel_to_s6e63j0x03(struct drm_panel *panel)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun return container_of(panel, struct s6e63j0x03, panel);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
s6e63j0x03_dcs_write_seq(struct s6e63j0x03 * ctx,const void * seq,size_t len)119*4882a593Smuzhiyun static inline ssize_t s6e63j0x03_dcs_write_seq(struct s6e63j0x03 *ctx,
120*4882a593Smuzhiyun const void *seq, size_t len)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun return mipi_dsi_dcs_write_buffer(dsi, seq, len);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun #define s6e63j0x03_dcs_write_seq_static(ctx, seq...) \
128*4882a593Smuzhiyun ({ \
129*4882a593Smuzhiyun static const u8 d[] = { seq }; \
130*4882a593Smuzhiyun s6e63j0x03_dcs_write_seq(ctx, d, ARRAY_SIZE(d)); \
131*4882a593Smuzhiyun })
132*4882a593Smuzhiyun
s6e63j0x03_enable_lv2_command(struct s6e63j0x03 * ctx)133*4882a593Smuzhiyun static inline int s6e63j0x03_enable_lv2_command(struct s6e63j0x03 *ctx)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun return s6e63j0x03_dcs_write_seq_static(ctx, MCS_LEVEL2_KEY, 0x5a, 0x5a);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
s6e63j0x03_apply_mtp_key(struct s6e63j0x03 * ctx,bool on)138*4882a593Smuzhiyun static inline int s6e63j0x03_apply_mtp_key(struct s6e63j0x03 *ctx, bool on)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun if (on)
141*4882a593Smuzhiyun return s6e63j0x03_dcs_write_seq_static(ctx,
142*4882a593Smuzhiyun MCS_MTP_KEY, 0x5a, 0x5a);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun return s6e63j0x03_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0xa5, 0xa5);
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
s6e63j0x03_power_on(struct s6e63j0x03 * ctx)147*4882a593Smuzhiyun static int s6e63j0x03_power_on(struct s6e63j0x03 *ctx)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun int ret;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
152*4882a593Smuzhiyun if (ret < 0)
153*4882a593Smuzhiyun return ret;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun msleep(30);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 1);
158*4882a593Smuzhiyun usleep_range(1000, 2000);
159*4882a593Smuzhiyun gpiod_set_value(ctx->reset_gpio, 0);
160*4882a593Smuzhiyun usleep_range(5000, 6000);
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return 0;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
s6e63j0x03_power_off(struct s6e63j0x03 * ctx)165*4882a593Smuzhiyun static int s6e63j0x03_power_off(struct s6e63j0x03 *ctx)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
s6e63j0x03_get_brightness_index(unsigned int brightness)170*4882a593Smuzhiyun static unsigned int s6e63j0x03_get_brightness_index(unsigned int brightness)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun unsigned int index;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun index = brightness / (MAX_BRIGHTNESS / NUM_GAMMA_STEPS);
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (index >= NUM_GAMMA_STEPS)
177*4882a593Smuzhiyun index = NUM_GAMMA_STEPS - 1;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun return index;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
s6e63j0x03_update_gamma(struct s6e63j0x03 * ctx,unsigned int brightness)182*4882a593Smuzhiyun static int s6e63j0x03_update_gamma(struct s6e63j0x03 *ctx,
183*4882a593Smuzhiyun unsigned int brightness)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun struct backlight_device *bl_dev = ctx->bl_dev;
186*4882a593Smuzhiyun unsigned int index = s6e63j0x03_get_brightness_index(brightness);
187*4882a593Smuzhiyun int ret;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, true);
190*4882a593Smuzhiyun if (ret < 0)
191*4882a593Smuzhiyun return ret;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq(ctx, gamma_tbl[index], GAMMA_CMD_CNT);
194*4882a593Smuzhiyun if (ret < 0)
195*4882a593Smuzhiyun return ret;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, false);
198*4882a593Smuzhiyun if (ret < 0)
199*4882a593Smuzhiyun return ret;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun bl_dev->props.brightness = brightness;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun return 0;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
s6e63j0x03_set_brightness(struct backlight_device * bl_dev)206*4882a593Smuzhiyun static int s6e63j0x03_set_brightness(struct backlight_device *bl_dev)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun struct s6e63j0x03 *ctx = bl_get_data(bl_dev);
209*4882a593Smuzhiyun unsigned int brightness = bl_dev->props.brightness;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun return s6e63j0x03_update_gamma(ctx, brightness);
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun static const struct backlight_ops s6e63j0x03_bl_ops = {
215*4882a593Smuzhiyun .update_status = s6e63j0x03_set_brightness,
216*4882a593Smuzhiyun };
217*4882a593Smuzhiyun
s6e63j0x03_disable(struct drm_panel * panel)218*4882a593Smuzhiyun static int s6e63j0x03_disable(struct drm_panel *panel)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
221*4882a593Smuzhiyun struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
222*4882a593Smuzhiyun int ret;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_display_off(dsi);
225*4882a593Smuzhiyun if (ret < 0)
226*4882a593Smuzhiyun return ret;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun ctx->bl_dev->props.power = FB_BLANK_NORMAL;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
231*4882a593Smuzhiyun if (ret < 0)
232*4882a593Smuzhiyun return ret;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun msleep(120);
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun return 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
s6e63j0x03_unprepare(struct drm_panel * panel)239*4882a593Smuzhiyun static int s6e63j0x03_unprepare(struct drm_panel *panel)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
242*4882a593Smuzhiyun int ret;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun ret = s6e63j0x03_power_off(ctx);
245*4882a593Smuzhiyun if (ret < 0)
246*4882a593Smuzhiyun return ret;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun return 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
s6e63j0x03_panel_init(struct s6e63j0x03 * ctx)253*4882a593Smuzhiyun static int s6e63j0x03_panel_init(struct s6e63j0x03 *ctx)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
256*4882a593Smuzhiyun int ret;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun ret = s6e63j0x03_enable_lv2_command(ctx);
259*4882a593Smuzhiyun if (ret < 0)
260*4882a593Smuzhiyun return ret;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, true);
263*4882a593Smuzhiyun if (ret < 0)
264*4882a593Smuzhiyun return ret;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* set porch adjustment */
267*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf2, 0x1c, 0x28);
268*4882a593Smuzhiyun if (ret < 0)
269*4882a593Smuzhiyun return ret;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun /* set frame freq */
272*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb5, 0x00, 0x02, 0x00);
273*4882a593Smuzhiyun if (ret < 0)
274*4882a593Smuzhiyun return ret;
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun /* set caset, paset */
277*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_column_address(dsi, FIRST_COLUMN,
278*4882a593Smuzhiyun default_mode.hdisplay - 1 + FIRST_COLUMN);
279*4882a593Smuzhiyun if (ret < 0)
280*4882a593Smuzhiyun return ret;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1);
283*4882a593Smuzhiyun if (ret < 0)
284*4882a593Smuzhiyun return ret;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun /* set ltps timming 0, 1 */
287*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf8, 0x08, 0x08, 0x08, 0x17,
288*4882a593Smuzhiyun 0x00, 0x2a, 0x02, 0x26, 0x00, 0x00, 0x02, 0x00, 0x00);
289*4882a593Smuzhiyun if (ret < 0)
290*4882a593Smuzhiyun return ret;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xf7, 0x02);
293*4882a593Smuzhiyun if (ret < 0)
294*4882a593Smuzhiyun return ret;
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun /* set param pos te_edge */
297*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x01);
298*4882a593Smuzhiyun if (ret < 0)
299*4882a593Smuzhiyun return ret;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /* set te rising edge */
302*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xe2, 0x0f);
303*4882a593Smuzhiyun if (ret < 0)
304*4882a593Smuzhiyun return ret;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun /* set param pos default */
307*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb0, 0x00);
308*4882a593Smuzhiyun if (ret < 0)
309*4882a593Smuzhiyun return ret;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
312*4882a593Smuzhiyun if (ret < 0)
313*4882a593Smuzhiyun return ret;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, false);
316*4882a593Smuzhiyun if (ret < 0)
317*4882a593Smuzhiyun return ret;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return 0;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
s6e63j0x03_prepare(struct drm_panel * panel)322*4882a593Smuzhiyun static int s6e63j0x03_prepare(struct drm_panel *panel)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
325*4882a593Smuzhiyun int ret;
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun ret = s6e63j0x03_power_on(ctx);
328*4882a593Smuzhiyun if (ret < 0)
329*4882a593Smuzhiyun return ret;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun ret = s6e63j0x03_panel_init(ctx);
332*4882a593Smuzhiyun if (ret < 0)
333*4882a593Smuzhiyun goto err;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun ctx->bl_dev->props.power = FB_BLANK_NORMAL;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun return 0;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun err:
340*4882a593Smuzhiyun s6e63j0x03_power_off(ctx);
341*4882a593Smuzhiyun return ret;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
s6e63j0x03_enable(struct drm_panel * panel)344*4882a593Smuzhiyun static int s6e63j0x03_enable(struct drm_panel *panel)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun struct s6e63j0x03 *ctx = panel_to_s6e63j0x03(panel);
347*4882a593Smuzhiyun struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
348*4882a593Smuzhiyun int ret;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun msleep(120);
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, true);
353*4882a593Smuzhiyun if (ret < 0)
354*4882a593Smuzhiyun return ret;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun /* set elvss_cond */
357*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx, 0xb1, 0x00, 0x09);
358*4882a593Smuzhiyun if (ret < 0)
359*4882a593Smuzhiyun return ret;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* set pos */
362*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx,
363*4882a593Smuzhiyun MIPI_DCS_SET_ADDRESS_MODE, 0x40);
364*4882a593Smuzhiyun if (ret < 0)
365*4882a593Smuzhiyun return ret;
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun /* set default white brightness */
368*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x00ff);
369*4882a593Smuzhiyun if (ret < 0)
370*4882a593Smuzhiyun return ret;
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun /* set white ctrl */
373*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx,
374*4882a593Smuzhiyun MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
375*4882a593Smuzhiyun if (ret < 0)
376*4882a593Smuzhiyun return ret;
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /* set acl off */
379*4882a593Smuzhiyun ret = s6e63j0x03_dcs_write_seq_static(ctx,
380*4882a593Smuzhiyun MIPI_DCS_WRITE_POWER_SAVE, 0x00);
381*4882a593Smuzhiyun if (ret < 0)
382*4882a593Smuzhiyun return ret;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
385*4882a593Smuzhiyun if (ret < 0)
386*4882a593Smuzhiyun return ret;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun ret = s6e63j0x03_apply_mtp_key(ctx, false);
389*4882a593Smuzhiyun if (ret < 0)
390*4882a593Smuzhiyun return ret;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun ret = mipi_dsi_dcs_set_display_on(dsi);
393*4882a593Smuzhiyun if (ret < 0)
394*4882a593Smuzhiyun return ret;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun ctx->bl_dev->props.power = FB_BLANK_UNBLANK;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun return 0;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun
s6e63j0x03_get_modes(struct drm_panel * panel,struct drm_connector * connector)401*4882a593Smuzhiyun static int s6e63j0x03_get_modes(struct drm_panel *panel,
402*4882a593Smuzhiyun struct drm_connector *connector)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun struct drm_display_mode *mode;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun mode = drm_mode_duplicate(connector->dev, &default_mode);
407*4882a593Smuzhiyun if (!mode) {
408*4882a593Smuzhiyun dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
409*4882a593Smuzhiyun default_mode.hdisplay, default_mode.vdisplay,
410*4882a593Smuzhiyun drm_mode_vrefresh(&default_mode));
411*4882a593Smuzhiyun return -ENOMEM;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun drm_mode_set_name(mode);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
417*4882a593Smuzhiyun drm_mode_probed_add(connector, mode);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun connector->display_info.width_mm = 29;
420*4882a593Smuzhiyun connector->display_info.height_mm = 29;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun return 1;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun static const struct drm_panel_funcs s6e63j0x03_funcs = {
426*4882a593Smuzhiyun .disable = s6e63j0x03_disable,
427*4882a593Smuzhiyun .unprepare = s6e63j0x03_unprepare,
428*4882a593Smuzhiyun .prepare = s6e63j0x03_prepare,
429*4882a593Smuzhiyun .enable = s6e63j0x03_enable,
430*4882a593Smuzhiyun .get_modes = s6e63j0x03_get_modes,
431*4882a593Smuzhiyun };
432*4882a593Smuzhiyun
s6e63j0x03_probe(struct mipi_dsi_device * dsi)433*4882a593Smuzhiyun static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun struct device *dev = &dsi->dev;
436*4882a593Smuzhiyun struct s6e63j0x03 *ctx;
437*4882a593Smuzhiyun int ret;
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun ctx = devm_kzalloc(dev, sizeof(struct s6e63j0x03), GFP_KERNEL);
440*4882a593Smuzhiyun if (!ctx)
441*4882a593Smuzhiyun return -ENOMEM;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun mipi_dsi_set_drvdata(dsi, ctx);
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun ctx->dev = dev;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun dsi->lanes = 1;
448*4882a593Smuzhiyun dsi->format = MIPI_DSI_FMT_RGB888;
449*4882a593Smuzhiyun dsi->mode_flags = MIPI_DSI_MODE_EOT_PACKET;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun ctx->supplies[0].supply = "vdd3";
452*4882a593Smuzhiyun ctx->supplies[1].supply = "vci";
453*4882a593Smuzhiyun ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
454*4882a593Smuzhiyun ctx->supplies);
455*4882a593Smuzhiyun if (ret < 0) {
456*4882a593Smuzhiyun dev_err(dev, "failed to get regulators: %d\n", ret);
457*4882a593Smuzhiyun return ret;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
461*4882a593Smuzhiyun if (IS_ERR(ctx->reset_gpio)) {
462*4882a593Smuzhiyun dev_err(dev, "cannot get reset-gpio: %ld\n",
463*4882a593Smuzhiyun PTR_ERR(ctx->reset_gpio));
464*4882a593Smuzhiyun return PTR_ERR(ctx->reset_gpio);
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,
468*4882a593Smuzhiyun DRM_MODE_CONNECTOR_DSI);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
471*4882a593Smuzhiyun &s6e63j0x03_bl_ops, NULL);
472*4882a593Smuzhiyun if (IS_ERR(ctx->bl_dev)) {
473*4882a593Smuzhiyun dev_err(dev, "failed to register backlight device\n");
474*4882a593Smuzhiyun return PTR_ERR(ctx->bl_dev);
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun ctx->bl_dev->props.max_brightness = MAX_BRIGHTNESS;
478*4882a593Smuzhiyun ctx->bl_dev->props.brightness = DEFAULT_BRIGHTNESS;
479*4882a593Smuzhiyun ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun drm_panel_add(&ctx->panel);
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun ret = mipi_dsi_attach(dsi);
484*4882a593Smuzhiyun if (ret < 0)
485*4882a593Smuzhiyun goto remove_panel;
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun return ret;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun remove_panel:
490*4882a593Smuzhiyun drm_panel_remove(&ctx->panel);
491*4882a593Smuzhiyun backlight_device_unregister(ctx->bl_dev);
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun return ret;
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun
s6e63j0x03_remove(struct mipi_dsi_device * dsi)496*4882a593Smuzhiyun static int s6e63j0x03_remove(struct mipi_dsi_device *dsi)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi);
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun mipi_dsi_detach(dsi);
501*4882a593Smuzhiyun drm_panel_remove(&ctx->panel);
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun backlight_device_unregister(ctx->bl_dev);
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun return 0;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun static const struct of_device_id s6e63j0x03_of_match[] = {
509*4882a593Smuzhiyun { .compatible = "samsung,s6e63j0x03" },
510*4882a593Smuzhiyun { }
511*4882a593Smuzhiyun };
512*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, s6e63j0x03_of_match);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun static struct mipi_dsi_driver s6e63j0x03_driver = {
515*4882a593Smuzhiyun .probe = s6e63j0x03_probe,
516*4882a593Smuzhiyun .remove = s6e63j0x03_remove,
517*4882a593Smuzhiyun .driver = {
518*4882a593Smuzhiyun .name = "panel_samsung_s6e63j0x03",
519*4882a593Smuzhiyun .of_match_table = s6e63j0x03_of_match,
520*4882a593Smuzhiyun },
521*4882a593Smuzhiyun };
522*4882a593Smuzhiyun module_mipi_dsi_driver(s6e63j0x03_driver);
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
525*4882a593Smuzhiyun MODULE_AUTHOR("Hoegeun Kwon <hoegeun.kwon@samsung.com>");
526*4882a593Smuzhiyun MODULE_DESCRIPTION("MIPI-DSI based s6e63j0x03 AMOLED LCD Panel Driver");
527*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
528