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-elida-kd35t133.c (8989B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Elida kd35t133 5.5" MIPI-DSI panel driver
      4 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
      5 *
      6 * based on
      7 *
      8 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
      9 * Copyright (C) Purism SPC 2019
     10 */
     11
     12#include <linux/delay.h>
     13#include <linux/gpio/consumer.h>
     14#include <linux/media-bus-format.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/regulator/consumer.h>
     18
     19#include <video/display_timing.h>
     20#include <video/mipi_display.h>
     21
     22#include <drm/drm_mipi_dsi.h>
     23#include <drm/drm_modes.h>
     24#include <drm/drm_panel.h>
     25
     26/* Manufacturer specific Commands send via DSI */
     27#define KD35T133_CMD_INTERFACEMODECTRL		0xb0
     28#define KD35T133_CMD_FRAMERATECTRL		0xb1
     29#define KD35T133_CMD_DISPLAYINVERSIONCTRL	0xb4
     30#define KD35T133_CMD_DISPLAYFUNCTIONCTRL	0xb6
     31#define KD35T133_CMD_POWERCONTROL1		0xc0
     32#define KD35T133_CMD_POWERCONTROL2		0xc1
     33#define KD35T133_CMD_VCOMCONTROL		0xc5
     34#define KD35T133_CMD_POSITIVEGAMMA		0xe0
     35#define KD35T133_CMD_NEGATIVEGAMMA		0xe1
     36#define KD35T133_CMD_SETIMAGEFUNCTION		0xe9
     37#define KD35T133_CMD_ADJUSTCONTROL3		0xf7
     38
     39struct kd35t133 {
     40	struct device *dev;
     41	struct drm_panel panel;
     42	struct gpio_desc *reset_gpio;
     43	struct regulator *vdd;
     44	struct regulator *iovcc;
     45	enum drm_panel_orientation orientation;
     46	bool prepared;
     47};
     48
     49static inline struct kd35t133 *panel_to_kd35t133(struct drm_panel *panel)
     50{
     51	return container_of(panel, struct kd35t133, panel);
     52}
     53
     54#define dsi_dcs_write_seq(dsi, cmd, seq...) do {			\
     55		static const u8 b[] = { cmd, seq };			\
     56		int ret;						\
     57		ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b));	\
     58		if (ret < 0)						\
     59			return ret;					\
     60	} while (0)
     61
     62static int kd35t133_init_sequence(struct kd35t133 *ctx)
     63{
     64	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
     65	struct device *dev = ctx->dev;
     66
     67	/*
     68	 * Init sequence was supplied by the panel vendor with minimal
     69	 * documentation.
     70	 */
     71	dsi_dcs_write_seq(dsi, KD35T133_CMD_POSITIVEGAMMA,
     72			  0x00, 0x13, 0x18, 0x04, 0x0f, 0x06, 0x3a, 0x56,
     73			  0x4d, 0x03, 0x0a, 0x06, 0x30, 0x3e, 0x0f);
     74	dsi_dcs_write_seq(dsi, KD35T133_CMD_NEGATIVEGAMMA,
     75			  0x00, 0x13, 0x18, 0x01, 0x11, 0x06, 0x38, 0x34,
     76			  0x4d, 0x06, 0x0d, 0x0b, 0x31, 0x37, 0x0f);
     77	dsi_dcs_write_seq(dsi, KD35T133_CMD_POWERCONTROL1, 0x18, 0x17);
     78	dsi_dcs_write_seq(dsi, KD35T133_CMD_POWERCONTROL2, 0x41);
     79	dsi_dcs_write_seq(dsi, KD35T133_CMD_VCOMCONTROL, 0x00, 0x1a, 0x80);
     80	dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x48);
     81	dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
     82	dsi_dcs_write_seq(dsi, KD35T133_CMD_INTERFACEMODECTRL, 0x00);
     83	dsi_dcs_write_seq(dsi, KD35T133_CMD_FRAMERATECTRL, 0xa0);
     84	dsi_dcs_write_seq(dsi, KD35T133_CMD_DISPLAYINVERSIONCTRL, 0x02);
     85	dsi_dcs_write_seq(dsi, KD35T133_CMD_DISPLAYFUNCTIONCTRL,
     86			  0x20, 0x02);
     87	dsi_dcs_write_seq(dsi, KD35T133_CMD_SETIMAGEFUNCTION, 0x00);
     88	dsi_dcs_write_seq(dsi, KD35T133_CMD_ADJUSTCONTROL3,
     89			  0xa9, 0x51, 0x2c, 0x82);
     90	mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_INVERT_MODE, NULL, 0);
     91
     92	dev_dbg(dev, "Panel init sequence done\n");
     93	return 0;
     94}
     95
     96static int kd35t133_unprepare(struct drm_panel *panel)
     97{
     98	struct kd35t133 *ctx = panel_to_kd35t133(panel);
     99	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
    100	int ret;
    101
    102	if (!ctx->prepared)
    103		return 0;
    104
    105	ret = mipi_dsi_dcs_set_display_off(dsi);
    106	if (ret < 0)
    107		dev_err(ctx->dev, "failed to set display off: %d\n", ret);
    108
    109	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
    110	if (ret < 0) {
    111		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
    112		return ret;
    113	}
    114
    115	regulator_disable(ctx->iovcc);
    116	regulator_disable(ctx->vdd);
    117
    118	ctx->prepared = false;
    119
    120	return 0;
    121}
    122
    123static int kd35t133_prepare(struct drm_panel *panel)
    124{
    125	struct kd35t133 *ctx = panel_to_kd35t133(panel);
    126	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
    127	int ret;
    128
    129	if (ctx->prepared)
    130		return 0;
    131
    132	dev_dbg(ctx->dev, "Resetting the panel\n");
    133	ret = regulator_enable(ctx->vdd);
    134	if (ret < 0) {
    135		dev_err(ctx->dev, "Failed to enable vdd supply: %d\n", ret);
    136		return ret;
    137	}
    138
    139	ret = regulator_enable(ctx->iovcc);
    140	if (ret < 0) {
    141		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
    142		goto disable_vdd;
    143	}
    144
    145	msleep(20);
    146
    147	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
    148	usleep_range(10, 20);
    149	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
    150
    151	msleep(20);
    152
    153	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
    154	if (ret < 0) {
    155		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
    156		goto disable_iovcc;
    157	}
    158
    159	msleep(250);
    160
    161	ret = kd35t133_init_sequence(ctx);
    162	if (ret < 0) {
    163		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
    164		goto disable_iovcc;
    165	}
    166
    167	ret = mipi_dsi_dcs_set_display_on(dsi);
    168	if (ret < 0) {
    169		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
    170		goto disable_iovcc;
    171	}
    172
    173	msleep(50);
    174
    175	ctx->prepared = true;
    176
    177	return 0;
    178
    179disable_iovcc:
    180	regulator_disable(ctx->iovcc);
    181disable_vdd:
    182	regulator_disable(ctx->vdd);
    183	return ret;
    184}
    185
    186static const struct drm_display_mode default_mode = {
    187	.hdisplay	= 320,
    188	.hsync_start	= 320 + 130,
    189	.hsync_end	= 320 + 130 + 4,
    190	.htotal		= 320 + 130 + 4 + 130,
    191	.vdisplay	= 480,
    192	.vsync_start	= 480 + 2,
    193	.vsync_end	= 480 + 2 + 1,
    194	.vtotal		= 480 + 2 + 1 + 2,
    195	.clock		= 17000,
    196	.width_mm	= 42,
    197	.height_mm	= 82,
    198};
    199
    200static int kd35t133_get_modes(struct drm_panel *panel,
    201				struct drm_connector *connector)
    202{
    203	struct kd35t133 *ctx = panel_to_kd35t133(panel);
    204	struct drm_display_mode *mode;
    205
    206	mode = drm_mode_duplicate(connector->dev, &default_mode);
    207	if (!mode) {
    208		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
    209			default_mode.hdisplay, default_mode.vdisplay,
    210			drm_mode_vrefresh(&default_mode));
    211		return -ENOMEM;
    212	}
    213
    214	drm_mode_set_name(mode);
    215
    216	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
    217	connector->display_info.width_mm = mode->width_mm;
    218	connector->display_info.height_mm = mode->height_mm;
    219	drm_mode_probed_add(connector, mode);
    220	drm_connector_set_panel_orientation(connector, ctx->orientation);
    221
    222	return 1;
    223}
    224
    225static const struct drm_panel_funcs kd35t133_funcs = {
    226	.unprepare	= kd35t133_unprepare,
    227	.prepare	= kd35t133_prepare,
    228	.get_modes	= kd35t133_get_modes,
    229};
    230
    231static int kd35t133_probe(struct mipi_dsi_device *dsi)
    232{
    233	struct device *dev = &dsi->dev;
    234	struct kd35t133 *ctx;
    235	int ret;
    236
    237	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
    238	if (!ctx)
    239		return -ENOMEM;
    240
    241	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
    242	if (IS_ERR(ctx->reset_gpio)) {
    243		dev_err(dev, "cannot get reset gpio\n");
    244		return PTR_ERR(ctx->reset_gpio);
    245	}
    246
    247	ctx->vdd = devm_regulator_get(dev, "vdd");
    248	if (IS_ERR(ctx->vdd)) {
    249		ret = PTR_ERR(ctx->vdd);
    250		if (ret != -EPROBE_DEFER)
    251			dev_err(dev, "Failed to request vdd regulator: %d\n", ret);
    252		return ret;
    253	}
    254
    255	ctx->iovcc = devm_regulator_get(dev, "iovcc");
    256	if (IS_ERR(ctx->iovcc)) {
    257		ret = PTR_ERR(ctx->iovcc);
    258		if (ret != -EPROBE_DEFER)
    259			dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
    260		return ret;
    261	}
    262
    263	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
    264	if (ret < 0) {
    265		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
    266		return ret;
    267	}
    268
    269	mipi_dsi_set_drvdata(dsi, ctx);
    270
    271	ctx->dev = dev;
    272
    273	dsi->lanes = 1;
    274	dsi->format = MIPI_DSI_FMT_RGB888;
    275	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
    276			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
    277			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
    278
    279	drm_panel_init(&ctx->panel, &dsi->dev, &kd35t133_funcs,
    280		       DRM_MODE_CONNECTOR_DSI);
    281
    282	ret = drm_panel_of_backlight(&ctx->panel);
    283	if (ret)
    284		return ret;
    285
    286	drm_panel_add(&ctx->panel);
    287
    288	ret = mipi_dsi_attach(dsi);
    289	if (ret < 0) {
    290		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
    291		drm_panel_remove(&ctx->panel);
    292		return ret;
    293	}
    294
    295	return 0;
    296}
    297
    298static void kd35t133_shutdown(struct mipi_dsi_device *dsi)
    299{
    300	struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
    301	int ret;
    302
    303	ret = drm_panel_unprepare(&ctx->panel);
    304	if (ret < 0)
    305		dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
    306
    307	ret = drm_panel_disable(&ctx->panel);
    308	if (ret < 0)
    309		dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
    310}
    311
    312static int kd35t133_remove(struct mipi_dsi_device *dsi)
    313{
    314	struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
    315	int ret;
    316
    317	kd35t133_shutdown(dsi);
    318
    319	ret = mipi_dsi_detach(dsi);
    320	if (ret < 0)
    321		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
    322
    323	drm_panel_remove(&ctx->panel);
    324
    325	return 0;
    326}
    327
    328static const struct of_device_id kd35t133_of_match[] = {
    329	{ .compatible = "elida,kd35t133" },
    330	{ /* sentinel */ }
    331};
    332MODULE_DEVICE_TABLE(of, kd35t133_of_match);
    333
    334static struct mipi_dsi_driver kd35t133_driver = {
    335	.driver = {
    336		.name = "panel-elida-kd35t133",
    337		.of_match_table = kd35t133_of_match,
    338	},
    339	.probe	= kd35t133_probe,
    340	.remove = kd35t133_remove,
    341	.shutdown = kd35t133_shutdown,
    342};
    343module_mipi_dsi_driver(kd35t133_driver);
    344
    345MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
    346MODULE_DESCRIPTION("DRM driver for Elida kd35t133 MIPI DSI panel");
    347MODULE_LICENSE("GPL v2");