cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

panel-samsung-s6e63j0x03.c (12501B)


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