Linux Audio

Check our new training course

Embedded Linux training

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