Linux Audio

Check our new training course

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