File Index
Files and directories
Sign in
FILE

panel-elida-kd35t133.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/panel/panel-elida-kd35t133.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Elida kd35t133 3.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};
 47
 48static inline struct kd35t133 *panel_to_kd35t133(struct drm_panel *panel)
 49{
 50	return container_of(panel, struct kd35t133, panel);
 51}
 52
 53static void kd35t133_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
 54{
 55	/*
 56	 * Init sequence was supplied by the panel vendor with minimal
 57	 * documentation.
 58	 */
 59	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_POSITIVEGAMMA,
 60				     0x00, 0x13, 0x18, 0x04, 0x0f, 0x06, 0x3a, 0x56,
 61				     0x4d, 0x03, 0x0a, 0x06, 0x30, 0x3e, 0x0f);
 62	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_NEGATIVEGAMMA,
 63				     0x00, 0x13, 0x18, 0x01, 0x11, 0x06, 0x38, 0x34,
 64				     0x4d, 0x06, 0x0d, 0x0b, 0x31, 0x37, 0x0f);
 65	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_POWERCONTROL1, 0x18, 0x17);
 66	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_POWERCONTROL2, 0x41);
 67	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_VCOMCONTROL, 0x00, 0x1a, 0x80);
 68	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x48);
 69	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
 70	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_INTERFACEMODECTRL, 0x00);
 71	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_FRAMERATECTRL, 0xa0);
 72	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_DISPLAYINVERSIONCTRL, 0x02);
 73	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_DISPLAYFUNCTIONCTRL,
 74				     0x20, 0x02);
 75	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_SETIMAGEFUNCTION, 0x00);
 76	mipi_dsi_dcs_write_seq_multi(dsi_ctx, KD35T133_CMD_ADJUSTCONTROL3,
 77				     0xa9, 0x51, 0x2c, 0x82);
 78	mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_ENTER_INVERT_MODE);
 79}
 80
 81static int kd35t133_unprepare(struct drm_panel *panel)
 82{
 83	struct kd35t133 *ctx = panel_to_kd35t133(panel);
 84	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
 85	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
 86
 87	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
 88	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
 89	if (dsi_ctx.accum_err)
 90		return dsi_ctx.accum_err;
 91
 92	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
 93
 94	regulator_disable(ctx->iovcc);
 95	regulator_disable(ctx->vdd);
 96
 97	return 0;
 98}
 99
100static int kd35t133_prepare(struct drm_panel *panel)
101{
102	struct kd35t133 *ctx = panel_to_kd35t133(panel);
103	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
104	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
105
106	dev_dbg(ctx->dev, "Resetting the panel\n");
107	dsi_ctx.accum_err = regulator_enable(ctx->vdd);
108	if (dsi_ctx.accum_err) {
109		dev_err(ctx->dev, "Failed to enable vdd supply: %d\n",
110			dsi_ctx.accum_err);
111		return dsi_ctx.accum_err;
112	}
113
114	dsi_ctx.accum_err = regulator_enable(ctx->iovcc);
115	if (dsi_ctx.accum_err) {
116		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n",
117			dsi_ctx.accum_err);
118		goto disable_vdd;
119	}
120
121	msleep(20);
122
123	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
124	usleep_range(10, 20);
125	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
126
127	msleep(20);
128
129	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
130	mipi_dsi_msleep(&dsi_ctx, 250);
131
132	kd35t133_init_sequence(&dsi_ctx);
133	if (!dsi_ctx.accum_err)
134		dev_dbg(ctx->dev, "Panel init sequence done\n");
135
136	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
137	mipi_dsi_msleep(&dsi_ctx, 50);
138
139	if (dsi_ctx.accum_err)
140		goto disable_iovcc;
141
142	return 0;
143
144disable_iovcc:
145	regulator_disable(ctx->iovcc);
146disable_vdd:
147	regulator_disable(ctx->vdd);
148	return dsi_ctx.accum_err;
149}
150
151static const struct drm_display_mode default_mode = {
152	.hdisplay	= 320,
153	.hsync_start	= 320 + 130,
154	.hsync_end	= 320 + 130 + 4,
155	.htotal		= 320 + 130 + 4 + 130,
156	.vdisplay	= 480,
157	.vsync_start	= 480 + 2,
158	.vsync_end	= 480 + 2 + 1,
159	.vtotal		= 480 + 2 + 1 + 2,
160	.clock		= 17000,
161	.width_mm	= 42,
162	.height_mm	= 82,
163};
164
165static int kd35t133_get_modes(struct drm_panel *panel,
166				struct drm_connector *connector)
167{
168	struct kd35t133 *ctx = panel_to_kd35t133(panel);
169	struct drm_display_mode *mode;
170
171	mode = drm_mode_duplicate(connector->dev, &default_mode);
172	if (!mode) {
173		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
174			default_mode.hdisplay, default_mode.vdisplay,
175			drm_mode_vrefresh(&default_mode));
176		return -ENOMEM;
177	}
178
179	drm_mode_set_name(mode);
180
181	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
182	connector->display_info.width_mm = mode->width_mm;
183	connector->display_info.height_mm = mode->height_mm;
184	drm_mode_probed_add(connector, mode);
185
186	return 1;
187}
188
189static enum drm_panel_orientation kd35t133_get_orientation(struct drm_panel *panel)
190{
191	struct kd35t133 *ctx = panel_to_kd35t133(panel);
192
193	return ctx->orientation;
194}
195
196static const struct drm_panel_funcs kd35t133_funcs = {
197	.unprepare	= kd35t133_unprepare,
198	.prepare	= kd35t133_prepare,
199	.get_modes	= kd35t133_get_modes,
200	.get_orientation = kd35t133_get_orientation,
201};
202
203static int kd35t133_probe(struct mipi_dsi_device *dsi)
204{
205	struct device *dev = &dsi->dev;
206	struct kd35t133 *ctx;
207	int ret;
208
209	ctx = devm_drm_panel_alloc(dev, struct kd35t133, panel,
210				   &kd35t133_funcs, DRM_MODE_CONNECTOR_DSI);
211	if (IS_ERR(ctx))
212		return PTR_ERR(ctx);
213
214	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
215	if (IS_ERR(ctx->reset_gpio)) {
216		dev_err(dev, "cannot get reset gpio\n");
217		return PTR_ERR(ctx->reset_gpio);
218	}
219
220	ctx->vdd = devm_regulator_get(dev, "vdd");
221	if (IS_ERR(ctx->vdd)) {
222		ret = PTR_ERR(ctx->vdd);
223		if (ret != -EPROBE_DEFER)
224			dev_err(dev, "Failed to request vdd regulator: %d\n", ret);
225		return ret;
226	}
227
228	ctx->iovcc = devm_regulator_get(dev, "iovcc");
229	if (IS_ERR(ctx->iovcc)) {
230		ret = PTR_ERR(ctx->iovcc);
231		if (ret != -EPROBE_DEFER)
232			dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
233		return ret;
234	}
235
236	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
237	if (ret < 0) {
238		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
239		return ret;
240	}
241
242	mipi_dsi_set_drvdata(dsi, ctx);
243
244	ctx->dev = dev;
245
246	dsi->lanes = 1;
247	dsi->format = MIPI_DSI_FMT_RGB888;
248	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
249			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
250			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
251
252	ret = drm_panel_of_backlight(&ctx->panel);
253	if (ret)
254		return ret;
255
256	drm_panel_add(&ctx->panel);
257
258	ret = mipi_dsi_attach(dsi);
259	if (ret < 0) {
260		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
261		drm_panel_remove(&ctx->panel);
262		return ret;
263	}
264
265	return 0;
266}
267
268static void kd35t133_remove(struct mipi_dsi_device *dsi)
269{
270	struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
271	int ret;
272
273	ret = mipi_dsi_detach(dsi);
274	if (ret < 0)
275		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
276
277	drm_panel_remove(&ctx->panel);
278}
279
280static const struct of_device_id kd35t133_of_match[] = {
281	{ .compatible = "elida,kd35t133" },
282	{ /* sentinel */ }
283};
284MODULE_DEVICE_TABLE(of, kd35t133_of_match);
285
286static struct mipi_dsi_driver kd35t133_driver = {
287	.driver = {
288		.name = "panel-elida-kd35t133",
289		.of_match_table = kd35t133_of_match,
290	},
291	.probe	= kd35t133_probe,
292	.remove = kd35t133_remove,
293};
294module_mipi_dsi_driver(kd35t133_driver);
295
296MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
297MODULE_DESCRIPTION("DRM driver for Elida kd35t133 MIPI DSI panel");
298MODULE_LICENSE("GPL v2");
~/ Read-only file preview.