Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2020 BSH Hausgerate GmbH
  4 */
  5
  6#include <linux/delay.h>
  7#include <linux/device.h>
  8#include <linux/err.h>
  9#include <linux/errno.h>
 10#include <linux/kernel.h>
 11#include <linux/module.h>
 12#include <linux/of.h>
 13
 14#include <linux/gpio/consumer.h>
 15#include <linux/regulator/consumer.h>
 16
 17#include <drm/drm_mipi_dsi.h>
 18#include <drm/drm_modes.h>
 19#include <drm/drm_panel.h>
 20
 21#include <video/mipi_display.h>
 22
 23#define ILI9805_EXTCMD_CMD_SET_ENABLE_REG	(0xff)
 24#define ILI9805_SETEXTC_PARAMETER1		(0xff)
 25#define ILI9805_SETEXTC_PARAMETER2		(0x98)
 26#define ILI9805_SETEXTC_PARAMETER3		(0x05)
 27
 28#define ILI9805_INSTR(_delay, ...) { \
 29		.delay = (_delay), \
 30		.len = sizeof((u8[]) {__VA_ARGS__}), \
 31		.data = (u8[]){__VA_ARGS__} \
 32	}
 33
 34struct ili9805_instr {
 35	size_t len;
 36	const u8 *data;
 37	u32 delay;
 38};
 39
 40struct ili9805_desc {
 41	const char *name;
 42	const struct ili9805_instr *init;
 43	const size_t init_length;
 44	const struct drm_display_mode *mode;
 45	u32 width_mm;
 46	u32 height_mm;
 47};
 48
 49struct ili9805 {
 50	struct drm_panel	panel;
 51	struct mipi_dsi_device	*dsi;
 52	const struct ili9805_desc	*desc;
 53
 54	struct regulator	*dvdd;
 55	struct regulator	*avdd;
 56	struct gpio_desc	*reset_gpio;
 57};
 58
 59static const struct ili9805_instr gpm1780a0_init[] = {
 60	ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
 61		      ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
 62	ILI9805_INSTR(100, 0xFD, 0x0F, 0x10, 0x44, 0x00),
 63	ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,
 64		      0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00),
 65	ILI9805_INSTR(0, 0xB8, 0x62),
 66	ILI9805_INSTR(0, 0xF1, 0x00),
 67	ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
 68	ILI9805_INSTR(0, 0xF3, 0x60, 0x83, 0x04),
 69	ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
 70	ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
 71	ILI9805_INSTR(0, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
 72		      0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
 73	ILI9805_INSTR(0, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
 74		      0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
 75	ILI9805_INSTR(10, 0xc1, 0x13, 0x39, 0x19, 0x06),
 76	ILI9805_INSTR(10, 0xc7, 0xe5),
 77	ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
 78	ILI9805_INSTR(10, 0xB4, 0x02),
 79	ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
 80	ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x08),
 81	ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
 82	ILI9805_INSTR(0, 0x20),
 83	ILI9805_INSTR(0, 0xB0, 0x01),
 84	ILI9805_INSTR(0, 0xB6, 0x31, 0x00, 0xef),
 85	ILI9805_INSTR(0, 0xDF, 0x23),
 86	ILI9805_INSTR(0, 0xB9, 0x02, 0x00),
 87};
 88
 89static const struct ili9805_instr tm041xdhg01_init[] = {
 90	ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
 91		      ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
 92	ILI9805_INSTR(100, 0xFD, 0x0F, 0x13, 0x44, 0x00),
 93	ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,
 94		      0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01),
 95	ILI9805_INSTR(0, 0xB8, 0x74),
 96	ILI9805_INSTR(0, 0xF1, 0x00),
 97	ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
 98	ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
 99	ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
100	ILI9805_INSTR(0, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
101		      0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
102	ILI9805_INSTR(0, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
103		      0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
104	ILI9805_INSTR(10, 0xc1, 0x15, 0x03, 0x03, 0x31),
105	ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
106	ILI9805_INSTR(10, 0xB4, 0x02),
107	ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
108	ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x0a),
109	ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
110	ILI9805_INSTR(0, 0x20),
111	ILI9805_INSTR(0, 0xB0, 0x00),
112	ILI9805_INSTR(0, 0xB6, 0x01),
113	ILI9805_INSTR(0, 0xc2, 0x11),
114	ILI9805_INSTR(0, 0x51, 0xFF),
115	ILI9805_INSTR(0, 0x53, 0x24),
116	ILI9805_INSTR(0, 0x55, 0x00),
117};
118
119static inline struct ili9805 *panel_to_ili9805(struct drm_panel *panel)
120{
121	return container_of(panel, struct ili9805, panel);
122}
123
124static int ili9805_power_on(struct ili9805 *ctx)
125{
126	struct mipi_dsi_device *dsi = ctx->dsi;
127	struct device *dev = &dsi->dev;
128	int ret;
129
130	ret = regulator_enable(ctx->avdd);
131	if (ret) {
132		dev_err(dev, "Failed to enable avdd regulator (%d)\n", ret);
133		return ret;
134	}
135
136	ret = regulator_enable(ctx->dvdd);
137	if (ret) {
138		dev_err(dev, "Failed to enable dvdd regulator (%d)\n", ret);
139		regulator_disable(ctx->avdd);
140		return ret;
141	}
142
143	gpiod_set_value(ctx->reset_gpio, 0);
144	usleep_range(5000, 10000);
145	gpiod_set_value(ctx->reset_gpio, 1);
146	msleep(120);
147
148	return 0;
149}
150
151static int ili9805_power_off(struct ili9805 *ctx)
152{
153	gpiod_set_value(ctx->reset_gpio, 0);
154	regulator_disable(ctx->dvdd);
155	regulator_disable(ctx->avdd);
156
157	return 0;
158}
159
160static int ili9805_activate(struct ili9805 *ctx)
161{
162	struct mipi_dsi_device *dsi = ctx->dsi;
163	struct device *dev = &dsi->dev;
164	int i, ret;
165
166	for (i = 0; i < ctx->desc->init_length; i++) {
167		const struct ili9805_instr *instr = &ctx->desc->init[i];
168
169		ret = mipi_dsi_dcs_write_buffer(ctx->dsi, instr->data, instr->len);
170		if (ret < 0)
171			return ret;
172
173		if (instr->delay > 0)
174			msleep(instr->delay);
175	}
176
177	ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
178	if (ret) {
179		dev_err(dev, "Failed to exit sleep mode (%d)\n", ret);
180		return ret;
181	}
182
183	usleep_range(5000, 6000);
184
185	ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
186	if (ret) {
187		dev_err(dev, "Failed to set display ON (%d)\n", ret);
188		return ret;
189	}
190
191	return 0;
192}
193
194static int ili9805_prepare(struct drm_panel *panel)
195{
196	struct ili9805 *ctx = panel_to_ili9805(panel);
197	int ret;
198
199	ret = ili9805_power_on(ctx);
200	if (ret)
201		return ret;
202
203	ret = ili9805_activate(ctx);
204	if (ret) {
205		ili9805_power_off(ctx);
206		return ret;
207	}
208
209	return 0;
210}
211
212static int ili9805_deactivate(struct ili9805 *ctx)
213{
214	struct mipi_dsi_device *dsi = ctx->dsi;
215	struct device *dev = &dsi->dev;
216	int ret;
217
218	ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
219	if (ret < 0) {
220		dev_err(dev, "Failed to set display OFF (%d)\n", ret);
221		return ret;
222	}
223
224	usleep_range(5000, 10000);
225
226	ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
227	if (ret < 0) {
228		dev_err(dev, "Failed to enter sleep mode (%d)\n", ret);
229		return ret;
230	}
231
232	return 0;
233}
234
235static int ili9805_unprepare(struct drm_panel *panel)
236{
237	struct ili9805 *ctx = panel_to_ili9805(panel);
238
239	ili9805_deactivate(ctx);
240	ili9805_power_off(ctx);
241
242	return 0;
243}
244
245static const struct drm_display_mode gpm1780a0_timing = {
246	.clock = 26227,
247
248	.hdisplay = 480,
249	.hsync_start = 480 + 10,
250	.hsync_end = 480 + 10 + 2,
251	.htotal = 480 + 10 + 2 + 36,
252
253	.vdisplay = 480,
254	.vsync_start = 480 + 2,
255	.vsync_end = 480 + 10 + 4,
256	.vtotal = 480 + 2 + 4 + 10,
257};
258
259static const struct drm_display_mode tm041xdhg01_timing = {
260	.clock = 26227,
261
262	.hdisplay = 480,
263	.hsync_start = 480 + 10,
264	.hsync_end = 480 + 10 + 2,
265	.htotal = 480 + 10 + 2 + 36,
266
267	.vdisplay = 768,
268	.vsync_start = 768 + 2,
269	.vsync_end = 768 + 10 + 4,
270	.vtotal = 768 + 2 + 4 + 10,
271};
272
273static int ili9805_get_modes(struct drm_panel *panel,
274			      struct drm_connector *connector)
275{
276	struct ili9805 *ctx = panel_to_ili9805(panel);
277	struct drm_display_mode *mode;
278
279	mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
280	if (!mode) {
281		dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
282			ctx->desc->mode->hdisplay,
283			ctx->desc->mode->vdisplay,
284			drm_mode_vrefresh(ctx->desc->mode));
285		return -ENOMEM;
286	}
287
288	drm_mode_set_name(mode);
289
290	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
291	drm_mode_probed_add(connector, mode);
292
293	connector->display_info.width_mm = mode->width_mm;
294	connector->display_info.height_mm = mode->height_mm;
295
296	return 1;
297}
298
299static const struct drm_panel_funcs ili9805_funcs = {
300	.prepare	= ili9805_prepare,
301	.unprepare	= ili9805_unprepare,
302	.get_modes	= ili9805_get_modes,
303};
304
305static int ili9805_dsi_probe(struct mipi_dsi_device *dsi)
306{
307	struct ili9805 *ctx;
308	int ret;
309
310	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
311	if (!ctx)
312		return -ENOMEM;
313	mipi_dsi_set_drvdata(dsi, ctx);
314	ctx->dsi = dsi;
315	ctx->desc = of_device_get_match_data(&dsi->dev);
316
317	dsi->format = MIPI_DSI_FMT_RGB888;
318	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
319		MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM |
320		MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_NO_EOT_PACKET;
321	dsi->lanes = 2;
322
323	drm_panel_init(&ctx->panel, &dsi->dev, &ili9805_funcs,
324		       DRM_MODE_CONNECTOR_DSI);
325
326	ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
327	if (IS_ERR(ctx->dvdd))
328		return PTR_ERR(ctx->dvdd);
329	ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");
330	if (IS_ERR(ctx->avdd))
331		return PTR_ERR(ctx->avdd);
332
333	ctx->reset_gpio = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
334	if (IS_ERR(ctx->reset_gpio)) {
335		dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
336		return PTR_ERR(ctx->reset_gpio);
337	}
338
339	ctx->panel.prepare_prev_first = true;
340	ret = drm_panel_of_backlight(&ctx->panel);
341	if (ret)
342		return ret;
343
344	drm_panel_add(&ctx->panel);
345
346	ret = mipi_dsi_attach(dsi);
347	if (ret < 0) {
348		dev_err(&dsi->dev, "mipi_dsi_attach failed: %d\n", ret);
349		drm_panel_remove(&ctx->panel);
350		return ret;
351	}
352
353	return 0;
354}
355
356static void ili9805_dsi_remove(struct mipi_dsi_device *dsi)
357{
358	struct ili9805 *ctx = mipi_dsi_get_drvdata(dsi);
359	int ret;
360
361	ret = mipi_dsi_detach(dsi);
362	if (ret < 0)
363		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
364			ret);
365
366	drm_panel_remove(&ctx->panel);
367}
368
369static const struct ili9805_desc gpm1780a0_desc = {
370	.init = gpm1780a0_init,
371	.init_length = ARRAY_SIZE(gpm1780a0_init),
372	.mode = &gpm1780a0_timing,
373	.width_mm = 65,
374	.height_mm = 65,
375};
376
377static const struct ili9805_desc tm041xdhg01_desc = {
378	.init = tm041xdhg01_init,
379	.init_length = ARRAY_SIZE(tm041xdhg01_init),
380	.mode = &tm041xdhg01_timing,
381	.width_mm = 42,
382	.height_mm = 96,
383};
384
385static const struct of_device_id ili9805_of_match[] = {
386	{ .compatible = "giantplus,gpm1790a0", .data = &gpm1780a0_desc },
387	{ .compatible = "tianma,tm041xdhg01", .data = &tm041xdhg01_desc },
388	{ }
389};
390MODULE_DEVICE_TABLE(of, ili9805_of_match);
391
392static struct mipi_dsi_driver ili9805_dsi_driver = {
393	.probe		= ili9805_dsi_probe,
394	.remove		= ili9805_dsi_remove,
395	.driver = {
396		.name		= "ili9805-dsi",
397		.of_match_table	= ili9805_of_match,
398	},
399};
400module_mipi_dsi_driver(ili9805_dsi_driver);
401
402MODULE_AUTHOR("Matthias Proske <Matthias.Proske@bshg.com>");
403MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
404MODULE_DESCRIPTION("Ilitek ILI9805 Controller Driver");
405MODULE_LICENSE("GPL");