File Index
Files and directories
Sign in
FILE

panel-samsung-s6e63m0.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * S6E63M0 AMOLED LCD drm_panel driver.
  4 *
  5 * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
  6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
  7 *
  8 * Andrzej Hajda <a.hajda@samsung.com>
  9 */
 10
 11#include <drm/drm_modes.h>
 12#include <drm/drm_panel.h>
 13
 14#include <linux/backlight.h>
 15#include <linux/delay.h>
 16#include <linux/export.h>
 17#include <linux/gpio/consumer.h>
 18#include <linux/module.h>
 19#include <linux/property.h>
 20#include <linux/regulator/consumer.h>
 21#include <linux/media-bus-format.h>
 22
 23#include <video/mipi_display.h>
 24
 25#include "panel-samsung-s6e63m0.h"
 26
 27#define S6E63M0_LCD_ID_VALUE_M2		0xA4
 28#define S6E63M0_LCD_ID_VALUE_SM2	0xB4
 29#define S6E63M0_LCD_ID_VALUE_SM2_1	0xB6
 30
 31#define NUM_GAMMA_LEVELS	28
 32#define GAMMA_TABLE_COUNT	23
 33
 34#define MAX_BRIGHTNESS		(NUM_GAMMA_LEVELS - 1)
 35
 36/* array of gamma tables for gamma value 2.2 */
 37static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
 38	/* 30 cd */
 39	{ MCS_PGAMMACTL, 0x02,
 40	  0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
 41	  0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
 42	  0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
 43	/* 40 cd */
 44	{ MCS_PGAMMACTL, 0x02,
 45	  0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
 46	  0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
 47	  0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
 48	/* 50 cd */
 49	{ MCS_PGAMMACTL, 0x02,
 50	  0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
 51	  0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
 52	  0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
 53	/* 60 cd */
 54	{ MCS_PGAMMACTL, 0x02,
 55	  0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
 56	  0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
 57	  0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
 58	/* 70 cd */
 59	{ MCS_PGAMMACTL, 0x02,
 60	  0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
 61	  0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
 62	  0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
 63	/* 80 cd */
 64	{ MCS_PGAMMACTL, 0x02,
 65	  0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
 66	  0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
 67	  0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
 68	/* 90 cd */
 69	{ MCS_PGAMMACTL, 0x02,
 70	  0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
 71	  0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
 72	  0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
 73	/* 100 cd */
 74	{ MCS_PGAMMACTL, 0x02,
 75	  0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
 76	  0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
 77	  0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
 78	/* 110 cd */
 79	{ MCS_PGAMMACTL, 0x02,
 80	  0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
 81	  0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
 82	  0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
 83	/* 120 cd */
 84	{ MCS_PGAMMACTL, 0x02,
 85	  0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
 86	  0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
 87	  0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
 88	/* 130 cd */
 89	{ MCS_PGAMMACTL, 0x02,
 90	  0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
 91	  0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
 92	  0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
 93	/* 140 cd */
 94	{ MCS_PGAMMACTL, 0x02,
 95	  0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
 96	  0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
 97	  0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
 98	/* 150 cd */
 99	{ MCS_PGAMMACTL, 0x02,
100	  0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
101	  0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
102	  0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
103	/* 160 cd */
104	{ MCS_PGAMMACTL, 0x02,
105	  0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
106	  0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
107	  0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
108	/* 170 cd */
109	{ MCS_PGAMMACTL, 0x02,
110	  0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
111	  0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
112	  0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
113	/* 180 cd */
114	{ MCS_PGAMMACTL, 0x02,
115	  0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
116	  0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
117	  0xBF,	0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
118	/* 190 cd */
119	{ MCS_PGAMMACTL, 0x02,
120	  0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
121	  0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
122	  0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
123	/* 200 cd */
124	{ MCS_PGAMMACTL, 0x02,
125	  0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
126	  0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
127	  0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
128	/* 210 cd */
129	{ MCS_PGAMMACTL, 0x02,
130	  0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
131	  0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
132	  0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
133	/* 220 cd */
134	{ MCS_PGAMMACTL, 0x02,
135	  0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
136	  0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
137	  0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
138	/* 230 cd */
139	{ MCS_PGAMMACTL, 0x02,
140	  0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
141	  0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
142	  0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
143	/* 240 cd */
144	{ MCS_PGAMMACTL, 0x02,
145	  0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
146	  0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
147	  0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
148	/* 250 cd */
149	{ MCS_PGAMMACTL, 0x02,
150	  0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
151	  0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
152	  0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
153	/* 260 cd */
154	{ MCS_PGAMMACTL, 0x02,
155	  0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
156	  0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
157	  0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
158	/* 270 cd */
159	{ MCS_PGAMMACTL, 0x02,
160	  0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
161	  0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
162	  0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
163	/* 280 cd */
164	{ MCS_PGAMMACTL, 0x02,
165	  0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
166	  0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
167	  0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
168	/* 290 cd */
169	{ MCS_PGAMMACTL, 0x02,
170	  0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
171	  0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
172	  0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
173	/* 300 cd */
174	{ MCS_PGAMMACTL, 0x02,
175	  0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
176	  0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
177	  0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
178};
179
180#define NUM_ACL_LEVELS 7
181#define ACL_TABLE_COUNT 28
182
183static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
184	/* NULL ACL */
185	{ MCS_BCMODE,
186	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189	  0x00, 0x00, 0x00 },
190	/* 40P ACL */
191	{ MCS_BCMODE,
192	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
193	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
194	  0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
195	  0x2B, 0x31, 0x36 },
196	/* 43P ACL */
197	{ MCS_BCMODE,
198	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
199	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
200	  0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
201	  0x2F, 0x34, 0x3A },
202	/* 45P ACL */
203	{ MCS_BCMODE,
204	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
205	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
206	  0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
207	  0x31, 0x37, 0x3D },
208	/* 47P ACL */
209	{ MCS_BCMODE,
210	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
211	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
212	  0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
213	  0x34, 0x3B, 0x41 },
214	/* 48P ACL */
215	{ MCS_BCMODE,
216	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
217	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
218	  0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
219	  0x36, 0x3C, 0x43 },
220	/* 50P ACL */
221	{ MCS_BCMODE,
222	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
223	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
224	  0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
225	  0x38, 0x3F, 0x46 },
226};
227
228/* This tells us which ACL level goes with which gamma */
229static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
230	/* 30 - 60 cd: ACL off/NULL */
231	0, 0, 0, 0,
232	/* 70 - 250 cd: 40P ACL */
233	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
234	/* 260 - 300 cd: 50P ACL */
235	6, 6, 6, 6, 6,
236};
237
238/* The ELVSS backlight regulator has 5 levels */
239#define S6E63M0_ELVSS_LEVELS 5
240
241static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
242	0x00,   /* not set */
243	0x0D,   /* 30 cd - 100 cd */
244	0x09,   /* 110 cd - 160 cd */
245	0x07,   /* 170 cd - 200 cd */
246	0x00,   /* 210 cd - 300 cd */
247};
248
249/* This tells us which ELVSS level goes with which gamma */
250static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
251	/* 30 - 100 cd */
252	1, 1, 1, 1, 1, 1, 1, 1,
253	/* 110 - 160 cd */
254	2, 2, 2, 2, 2, 2,
255	/* 170 - 200 cd */
256	3, 3, 3, 3,
257	/* 210 - 300 cd */
258	4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
259};
260
261struct s6e63m0 {
262	struct device *dev;
263	void *transport_data;
264	int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
265	int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
266	struct drm_panel panel;
267	struct backlight_device *bl_dev;
268	u8 lcd_type;
269	u8 elvss_pulse;
270	bool dsi_mode;
271
272	struct regulator_bulk_data supplies[2];
273	struct gpio_desc *reset_gpio;
274
275	/*
276	 * This field is tested by functions directly accessing bus before
277	 * transfer, transfer is skipped if it is set. In case of transfer
278	 * failure or unexpected response the field is set to error value.
279	 * Such construct allows to eliminate many checks in higher level
280	 * functions.
281	 */
282	int error;
283};
284
285static const struct drm_display_mode default_mode = {
286	.clock		= 25628,
287	.hdisplay	= 480,
288	.hsync_start	= 480 + 16,
289	.hsync_end	= 480 + 16 + 2,
290	.htotal		= 480 + 16 + 2 + 16,
291	.vdisplay	= 800,
292	.vsync_start	= 800 + 28,
293	.vsync_end	= 800 + 28 + 2,
294	.vtotal		= 800 + 28 + 2 + 1,
295	.width_mm	= 53,
296	.height_mm	= 89,
297	.flags		= DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
298};
299
300static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
301{
302	return container_of(panel, struct s6e63m0, panel);
303}
304
305static int s6e63m0_clear_error(struct s6e63m0 *ctx)
306{
307	int ret = ctx->error;
308
309	ctx->error = 0;
310	return ret;
311}
312
313static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
314{
315	if (ctx->error < 0)
316		return;
317
318	ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
319}
320
321static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
322{
323	if (ctx->error < 0 || len == 0)
324		return;
325
326	ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
327}
328
329#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
330	({ \
331		static const u8 d[] = { seq }; \
332		s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
333	})
334
335static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
336{
337	u8 id1, id2, id3;
338	int ret;
339
340	s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
341	s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
342	s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
343
344	ret = s6e63m0_clear_error(ctx);
345	if (ret) {
346		dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
347		ctx->lcd_type = 0x00;
348		return ret;
349	}
350
351	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
352
353	/*
354	 * We attempt to detect what panel is mounted on the controller.
355	 * The third ID byte represents the desired ELVSS pulse for
356	 * some displays.
357	 */
358	switch (id2) {
359	case S6E63M0_LCD_ID_VALUE_M2:
360		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
361		ctx->elvss_pulse = id3;
362		break;
363	case S6E63M0_LCD_ID_VALUE_SM2:
364	case S6E63M0_LCD_ID_VALUE_SM2_1:
365		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
366		ctx->elvss_pulse = id3;
367		break;
368	default:
369		dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
370		/* Default ELVSS pulse level */
371		ctx->elvss_pulse = 0x16;
372		break;
373	}
374
375	ctx->lcd_type = id2;
376
377	return 0;
378}
379
380static void s6e63m0_init(struct s6e63m0 *ctx)
381{
382	/*
383	 * We do not know why there is a difference in the DSI mode.
384	 * (No datasheet.)
385	 *
386	 * In the vendor driver this sequence is called
387	 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
388	 */
389	if (ctx->dsi_mode)
390		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
391					     0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
392					     0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
393	else
394		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
395					     0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
396					     0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
397
398	s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
399				     0x02, 0x03, 0x1c, 0x10, 0x10);
400	s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
401				     0x03, 0x00, 0x00);
402
403	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
404				     0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
405				     0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
406				     0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
407				     0xd6);
408	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
409				     0x01);
410
411	s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
412				     0x00, 0x8e, 0x07);
413	s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
414
415	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
416				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
417				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
418				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
419				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
420				     0x21, 0x20, 0x1e, 0x1e);
421
422	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
423				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
424				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
425				     0x66, 0x66);
426
427	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
428				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
429				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
430				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
431				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
432				     0x21, 0x20, 0x1e, 0x1e);
433
434	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
435				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
436				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
437				     0x66, 0x66);
438
439	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
440				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
441				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
442				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
443				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
444				     0x21, 0x20, 0x1e, 0x1e);
445
446	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
447				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
448				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
449				     0x66, 0x66);
450
451	s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
452				     0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
453				     0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
454				     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
455				     0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
456
457	s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
458				     0x10, 0x10, 0x0b, 0x05);
459
460	s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
461				     0x01);
462
463	s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
464				     0x0b);
465}
466
467static int s6e63m0_power_on(struct s6e63m0 *ctx)
468{
469	int ret;
470
471	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
472	if (ret < 0)
473		return ret;
474
475	msleep(25);
476
477	/* Be sure to send a reset pulse */
478	gpiod_set_value(ctx->reset_gpio, 1);
479	msleep(5);
480	gpiod_set_value(ctx->reset_gpio, 0);
481	msleep(120);
482
483	return 0;
484}
485
486static int s6e63m0_power_off(struct s6e63m0 *ctx)
487{
488	int ret;
489
490	gpiod_set_value(ctx->reset_gpio, 1);
491	msleep(120);
492
493	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
494	if (ret < 0)
495		return ret;
496
497	return 0;
498}
499
500static int s6e63m0_disable(struct drm_panel *panel)
501{
502	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
503
504	backlight_disable(ctx->bl_dev);
505
506	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
507	msleep(10);
508	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
509	msleep(120);
510
511	return 0;
512}
513
514static int s6e63m0_unprepare(struct drm_panel *panel)
515{
516	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
517	int ret;
518
519	s6e63m0_clear_error(ctx);
520
521	ret = s6e63m0_power_off(ctx);
522	if (ret < 0)
523		return ret;
524
525	return 0;
526}
527
528static int s6e63m0_prepare(struct drm_panel *panel)
529{
530	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
531	int ret;
532
533	ret = s6e63m0_power_on(ctx);
534	if (ret < 0)
535		return ret;
536
537	/* Magic to unlock level 2 control of the display */
538	s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
539	/* Magic to unlock MTP reading */
540	s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
541
542	ret = s6e63m0_check_lcd_type(ctx);
543	if (ret < 0)
544		return ret;
545
546	s6e63m0_init(ctx);
547
548	ret = s6e63m0_clear_error(ctx);
549
550	if (ret < 0)
551		s6e63m0_unprepare(panel);
552
553	return ret;
554}
555
556static int s6e63m0_enable(struct drm_panel *panel)
557{
558	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
559
560	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
561	msleep(120);
562	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
563	msleep(10);
564
565	s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
566				     0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
567				     0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
568				     0x0F, 0x00);
569
570	backlight_enable(ctx->bl_dev);
571
572	return 0;
573}
574
575static int s6e63m0_get_modes(struct drm_panel *panel,
576			     struct drm_connector *connector)
577{
578	struct drm_display_mode *mode;
579	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
580
581	mode = drm_mode_duplicate(connector->dev, &default_mode);
582	if (!mode) {
583		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
584			default_mode.hdisplay, default_mode.vdisplay,
585			drm_mode_vrefresh(&default_mode));
586		return -ENOMEM;
587	}
588
589	connector->display_info.width_mm = mode->width_mm;
590	connector->display_info.height_mm = mode->height_mm;
591	drm_display_info_set_bus_formats(&connector->display_info,
592					 &bus_format, 1);
593	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
594		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
595
596	drm_mode_set_name(mode);
597
598	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
599	drm_mode_probed_add(connector, mode);
600
601	return 1;
602}
603
604static const struct drm_panel_funcs s6e63m0_drm_funcs = {
605	.disable	= s6e63m0_disable,
606	.unprepare	= s6e63m0_unprepare,
607	.prepare	= s6e63m0_prepare,
608	.enable		= s6e63m0_enable,
609	.get_modes	= s6e63m0_get_modes,
610};
611
612static int s6e63m0_set_brightness(struct backlight_device *bd)
613{
614	struct s6e63m0 *ctx = bl_get_data(bd);
615	int brightness = bd->props.brightness;
616	u8 elvss_val;
617	u8 elvss_cmd_set[5];
618	int i;
619
620	/* Adjust ELVSS to candela level */
621	i = s6e63m0_elvss_per_gamma[brightness];
622	elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
623	if (elvss_val > 0x1f)
624		elvss_val = 0x1f;
625	elvss_cmd_set[0] = MCS_TEMP_SWIRE;
626	elvss_cmd_set[1] = elvss_val;
627	elvss_cmd_set[2] = elvss_val;
628	elvss_cmd_set[3] = elvss_val;
629	elvss_cmd_set[4] = elvss_val;
630	s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
631
632	/* Update the ACL per gamma value */
633	i = s6e63m0_acl_per_gamma[brightness];
634	s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
635			  ARRAY_SIZE(s6e63m0_acl[i]));
636
637	/* Update gamma table */
638	s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
639			  ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
640	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
641
642
643	return s6e63m0_clear_error(ctx);
644}
645
646static const struct backlight_ops s6e63m0_backlight_ops = {
647	.update_status	= s6e63m0_set_brightness,
648};
649
650static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
651{
652	struct backlight_properties props = {
653		.type		= BACKLIGHT_RAW,
654		.brightness	= max_brightness,
655		.max_brightness = max_brightness,
656	};
657	struct device *dev = ctx->dev;
658	int ret = 0;
659
660	ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
661						     &s6e63m0_backlight_ops,
662						     &props);
663	if (IS_ERR(ctx->bl_dev)) {
664		ret = PTR_ERR(ctx->bl_dev);
665		dev_err(dev, "error registering backlight device (%d)\n", ret);
666	}
667
668	return ret;
669}
670
671int s6e63m0_probe(struct device *dev, void *trsp,
672		  int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
673		  int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
674		  bool dsi_mode)
675{
676	struct s6e63m0 *ctx;
677	u32 max_brightness;
678	int ret;
679
680	ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
681	if (!ctx)
682		return -ENOMEM;
683
684	ctx->transport_data = trsp;
685	ctx->dsi_mode = dsi_mode;
686	ctx->dcs_read = dcs_read;
687	ctx->dcs_write = dcs_write;
688	dev_set_drvdata(dev, ctx);
689
690	ctx->dev = dev;
691
692	ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
693	if (ret)
694		max_brightness = MAX_BRIGHTNESS;
695	if (max_brightness > MAX_BRIGHTNESS) {
696		dev_err(dev, "illegal max brightness specified\n");
697		max_brightness = MAX_BRIGHTNESS;
698	}
699
700	ctx->supplies[0].supply = "vdd3";
701	ctx->supplies[1].supply = "vci";
702	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
703				      ctx->supplies);
704	if (ret < 0) {
705		dev_err(dev, "failed to get regulators: %d\n", ret);
706		return ret;
707	}
708
709	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
710	if (IS_ERR(ctx->reset_gpio)) {
711		dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
712		return PTR_ERR(ctx->reset_gpio);
713	}
714
715	drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
716		       dsi_mode ? DRM_MODE_CONNECTOR_DSI :
717		       DRM_MODE_CONNECTOR_DPI);
718
719	ret = s6e63m0_backlight_register(ctx, max_brightness);
720	if (ret < 0)
721		return ret;
722
723	drm_panel_add(&ctx->panel);
724
725	return 0;
726}
727EXPORT_SYMBOL_GPL(s6e63m0_probe);
728
729void s6e63m0_remove(struct device *dev)
730{
731	struct s6e63m0 *ctx = dev_get_drvdata(dev);
732
733	drm_panel_remove(&ctx->panel);
734}
735EXPORT_SYMBOL_GPL(s6e63m0_remove);
736
737MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
738MODULE_DESCRIPTION("s6e63m0 LCD Driver");
739MODULE_LICENSE("GPL v2");
~/ Read-only file preview.