Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
210	if (!ctx)
211		return -ENOMEM;
212
213	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
214	if (IS_ERR(ctx->reset_gpio)) {
215		dev_err(dev, "cannot get reset gpio\n");
216		return PTR_ERR(ctx->reset_gpio);
217	}
218
219	ctx->vdd = devm_regulator_get(dev, "vdd");
220	if (IS_ERR(ctx->vdd)) {
221		ret = PTR_ERR(ctx->vdd);
222		if (ret != -EPROBE_DEFER)
223			dev_err(dev, "Failed to request vdd regulator: %d\n", ret);
224		return ret;
225	}
226
227	ctx->iovcc = devm_regulator_get(dev, "iovcc");
228	if (IS_ERR(ctx->iovcc)) {
229		ret = PTR_ERR(ctx->iovcc);
230		if (ret != -EPROBE_DEFER)
231			dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
232		return ret;
233	}
234
235	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
236	if (ret < 0) {
237		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
238		return ret;
239	}
240
241	mipi_dsi_set_drvdata(dsi, ctx);
242
243	ctx->dev = dev;
244
245	dsi->lanes = 1;
246	dsi->format = MIPI_DSI_FMT_RGB888;
247	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
248			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
249			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
250
251	drm_panel_init(&ctx->panel, &dsi->dev, &kd35t133_funcs,
252		       DRM_MODE_CONNECTOR_DSI);
253
254	ret = drm_panel_of_backlight(&ctx->panel);
255	if (ret)
256		return ret;
257
258	drm_panel_add(&ctx->panel);
259
260	ret = mipi_dsi_attach(dsi);
261	if (ret < 0) {
262		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
263		drm_panel_remove(&ctx->panel);
264		return ret;
265	}
266
267	return 0;
268}
269
270static void kd35t133_remove(struct mipi_dsi_device *dsi)
271{
272	struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
273	int ret;
274
275	ret = mipi_dsi_detach(dsi);
276	if (ret < 0)
277		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
278
279	drm_panel_remove(&ctx->panel);
280}
281
282static const struct of_device_id kd35t133_of_match[] = {
283	{ .compatible = "elida,kd35t133" },
284	{ /* sentinel */ }
285};
286MODULE_DEVICE_TABLE(of, kd35t133_of_match);
287
288static struct mipi_dsi_driver kd35t133_driver = {
289	.driver = {
290		.name = "panel-elida-kd35t133",
291		.of_match_table = kd35t133_of_match,
292	},
293	.probe	= kd35t133_probe,
294	.remove = kd35t133_remove,
295};
296module_mipi_dsi_driver(kd35t133_driver);
297
298MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
299MODULE_DESCRIPTION("DRM driver for Elida kd35t133 MIPI DSI panel");
300MODULE_LICENSE("GPL v2");