1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2016 InforceComputing
4 * Copyright (C) 2016 Linaro Ltd
5 * Copyright (C) 2023 BayLibre, SAS
6 *
7 * Authors:
8 * - Vinay Simha BN <simhavcs@gmail.com>
9 * - Sumit Semwal <sumit.semwal@linaro.org>
10 * - Guillaume La Roque <glaroque@baylibre.com>
11 *
12 */
13
14#include <linux/backlight.h>
15#include <linux/delay.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/regulator/consumer.h>
20
21#include <video/mipi_display.h>
22
23#include <drm/drm_mipi_dsi.h>
24#include <drm/drm_modes.h>
25#include <drm/drm_panel.h>
26
27#define DSI_REG_MCAP	0xB0
28#define DSI_REG_IS	0xB3 /* Interface Setting */
29#define DSI_REG_IIS	0xB4 /* Interface ID Setting */
30#define DSI_REG_CTRL	0xB6
31
32enum {
33	IOVCC = 0,
34	POWER = 1
35};
36
37struct stk_panel {
38	bool prepared;
39	const struct drm_display_mode *mode;
40	struct backlight_device *backlight;
41	struct drm_panel base;
42	struct gpio_desc *enable_gpio; /* Power IC supply enable */
43	struct gpio_desc *reset_gpio; /* External reset */
44	struct mipi_dsi_device *dsi;
45	struct regulator_bulk_data supplies[2];
46};
47
48static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
49{
50	return container_of(panel, struct stk_panel, base);
51}
52
53static int stk_panel_init(struct stk_panel *stk)
54{
55	struct mipi_dsi_device *dsi = stk->dsi;
56	struct device *dev = &stk->dsi->dev;
57	int ret;
58
59	ret = mipi_dsi_dcs_soft_reset(dsi);
60	if (ret < 0) {
61		dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret);
62		return ret;
63	}
64	mdelay(5);
65
66	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
67	if (ret < 0) {
68		dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
69		return ret;
70	}
71	msleep(120);
72
73	mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04);
74
75	/* Interface setting, video mode */
76	mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
77	mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00);
78	mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3);
79
80	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77);
81	if (ret < 0) {
82		dev_err(dev, "failed to write display brightness: %d\n", ret);
83		return ret;
84	}
85
86	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
87			       MIPI_DCS_WRITE_MEMORY_START);
88
89	ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
90	if (ret < 0) {
91		dev_err(dev, "failed to set pixel format: %d\n", ret);
92		return ret;
93	}
94
95	ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1);
96	if (ret < 0) {
97		dev_err(dev, "failed to set column address: %d\n", ret);
98		return ret;
99	}
100
101	ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1);
102	if (ret < 0) {
103		dev_err(dev, "failed to set page address: %d\n", ret);
104		return ret;
105	}
106
107	return 0;
108}
109
110static int stk_panel_on(struct stk_panel *stk)
111{
112	struct mipi_dsi_device *dsi = stk->dsi;
113	struct device *dev = &stk->dsi->dev;
114	int ret;
115
116	ret = mipi_dsi_dcs_set_display_on(dsi);
117	if (ret < 0)
118		dev_err(dev, "failed to set display on: %d\n", ret);
119
120	mdelay(20);
121
122	return ret;
123}
124
125static void stk_panel_off(struct stk_panel *stk)
126{
127	struct mipi_dsi_device *dsi = stk->dsi;
128	struct device *dev = &stk->dsi->dev;
129	int ret;
130
131	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
132
133	ret = mipi_dsi_dcs_set_display_off(dsi);
134	if (ret < 0)
135		dev_err(dev, "failed to set display off: %d\n", ret);
136
137	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
138	if (ret < 0)
139		dev_err(dev, "failed to enter sleep mode: %d\n", ret);
140
141	msleep(100);
142}
143
144static int stk_panel_unprepare(struct drm_panel *panel)
145{
146	struct stk_panel *stk = to_stk_panel(panel);
147
148	if (!stk->prepared)
149		return 0;
150
151	stk_panel_off(stk);
152	regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
153	gpiod_set_value(stk->reset_gpio, 0);
154	gpiod_set_value(stk->enable_gpio, 1);
155
156	stk->prepared = false;
157
158	return 0;
159}
160
161static int stk_panel_prepare(struct drm_panel *panel)
162{
163	struct stk_panel *stk = to_stk_panel(panel);
164	struct device *dev = &stk->dsi->dev;
165	int ret;
166
167	if (stk->prepared)
168		return 0;
169
170	gpiod_set_value(stk->reset_gpio, 0);
171	gpiod_set_value(stk->enable_gpio, 0);
172	ret = regulator_enable(stk->supplies[IOVCC].consumer);
173	if (ret < 0)
174		return ret;
175
176	mdelay(8);
177	ret = regulator_enable(stk->supplies[POWER].consumer);
178	if (ret < 0)
179		goto iovccoff;
180
181	mdelay(20);
182	gpiod_set_value(stk->enable_gpio, 1);
183	mdelay(20);
184	gpiod_set_value(stk->reset_gpio, 1);
185	mdelay(10);
186	ret = stk_panel_init(stk);
187	if (ret < 0) {
188		dev_err(dev, "failed to init panel: %d\n", ret);
189		goto poweroff;
190	}
191
192	ret = stk_panel_on(stk);
193	if (ret < 0) {
194		dev_err(dev, "failed to set panel on: %d\n", ret);
195		goto poweroff;
196	}
197
198	stk->prepared = true;
199
200	return 0;
201
202poweroff:
203	regulator_disable(stk->supplies[POWER].consumer);
204iovccoff:
205	regulator_disable(stk->supplies[IOVCC].consumer);
206	gpiod_set_value(stk->reset_gpio, 0);
207	gpiod_set_value(stk->enable_gpio, 0);
208
209	return ret;
210}
211
212static const struct drm_display_mode default_mode = {
213		.clock = 163204,
214		.hdisplay = 1200,
215		.hsync_start = 1200 + 144,
216		.hsync_end = 1200 + 144 + 16,
217		.htotal = 1200 + 144 + 16 + 45,
218		.vdisplay = 1920,
219		.vsync_start = 1920 + 8,
220		.vsync_end = 1920 + 8 + 4,
221		.vtotal = 1920 + 8 + 4 + 4,
222		.width_mm = 95,
223		.height_mm = 151,
224};
225
226static int stk_panel_get_modes(struct drm_panel *panel,
227			       struct drm_connector *connector)
228{
229	struct drm_display_mode *mode;
230
231	mode = drm_mode_duplicate(connector->dev, &default_mode);
232	if (!mode) {
233		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
234			default_mode.hdisplay, default_mode.vdisplay,
235			drm_mode_vrefresh(&default_mode));
236		return -ENOMEM;
237	}
238
239	drm_mode_set_name(mode);
240	drm_mode_probed_add(connector, mode);
241	connector->display_info.width_mm = default_mode.width_mm;
242	connector->display_info.height_mm = default_mode.height_mm;
243	return 1;
244}
245
246static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
247{
248	struct mipi_dsi_device *dsi = bl_get_data(bl);
249	int ret;
250	u16 brightness;
251
252	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
253	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
254	if (ret < 0)
255		return ret;
256
257	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
258	return brightness & 0xff;
259}
260
261static int dsi_dcs_bl_update_status(struct backlight_device *bl)
262{
263	struct mipi_dsi_device *dsi = bl_get_data(bl);
264	struct device *dev = &dsi->dev;
265	int ret;
266
267	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
268	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
269	if (ret < 0) {
270		dev_err(dev, "failed to set DSI control: %d\n", ret);
271		return ret;
272	}
273
274	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
275	return 0;
276}
277
278static const struct backlight_ops dsi_bl_ops = {
279	.update_status = dsi_dcs_bl_update_status,
280	.get_brightness = dsi_dcs_bl_get_brightness,
281};
282
283static struct backlight_device *
284drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
285{
286	struct device *dev = &dsi->dev;
287	struct backlight_properties props = {
288		.type = BACKLIGHT_RAW,
289		.brightness = 255,
290		.max_brightness = 255,
291	};
292
293	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
294					      &dsi_bl_ops, &props);
295}
296
297static const struct drm_panel_funcs stk_panel_funcs = {
298	.unprepare = stk_panel_unprepare,
299	.prepare = stk_panel_prepare,
300	.get_modes = stk_panel_get_modes,
301};
302
303static const struct of_device_id stk_of_match[] = {
304	{ .compatible = "startek,kd070fhfid015", },
305	{ }
306};
307MODULE_DEVICE_TABLE(of, stk_of_match);
308
309static int stk_panel_add(struct stk_panel *stk)
310{
311	struct device *dev = &stk->dsi->dev;
312	int ret;
313
314	stk->mode = &default_mode;
315
316	stk->supplies[IOVCC].supply = "iovcc";
317	stk->supplies[POWER].supply = "power";
318	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
319	if (ret) {
320		dev_err(dev, "regulator_bulk failed\n");
321		return ret;
322	}
323
324	stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
325	if (IS_ERR(stk->reset_gpio)) {
326		ret = PTR_ERR(stk->reset_gpio);
327		dev_err(dev, "cannot get reset-gpios %d\n", ret);
328		return ret;
329	}
330
331	stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
332	if (IS_ERR(stk->enable_gpio)) {
333		ret = PTR_ERR(stk->enable_gpio);
334		dev_err(dev, "cannot get enable-gpio %d\n", ret);
335		return ret;
336	}
337
338	stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
339	if (IS_ERR(stk->backlight)) {
340		ret = PTR_ERR(stk->backlight);
341		dev_err(dev, "failed to register backlight %d\n", ret);
342		return ret;
343	}
344
345	drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
346		       DRM_MODE_CONNECTOR_DSI);
347
348	drm_panel_add(&stk->base);
349
350	return 0;
351}
352
353static int stk_panel_probe(struct mipi_dsi_device *dsi)
354{
355	struct stk_panel *stk;
356	int ret;
357
358	dsi->lanes = 4;
359	dsi->format = MIPI_DSI_FMT_RGB888;
360	dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
361
362	stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
363	if (!stk)
364		return -ENOMEM;
365
366	mipi_dsi_set_drvdata(dsi, stk);
367
368	stk->dsi = dsi;
369
370	ret = stk_panel_add(stk);
371	if (ret < 0)
372		return ret;
373
374	ret = mipi_dsi_attach(dsi);
375	if (ret < 0)
376		drm_panel_remove(&stk->base);
377
378	return 0;
379}
380
381static void stk_panel_remove(struct mipi_dsi_device *dsi)
382{
383	struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
384	int err;
385
386	err = mipi_dsi_detach(dsi);
387	if (err < 0)
388		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
389			err);
390
391	drm_panel_remove(&stk->base);
392}
393
394static struct mipi_dsi_driver stk_panel_driver = {
395	.driver = {
396		.name = "panel-startek-kd070fhfid015",
397		.of_match_table = stk_of_match,
398	},
399	.probe = stk_panel_probe,
400	.remove = stk_panel_remove,
401};
402module_mipi_dsi_driver(stk_panel_driver);
403
404MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
405MODULE_DESCRIPTION("STARTEK KD070FHFID015");
406MODULE_LICENSE("GPL");
407