1195534Sscottl// SPDX-License-Identifier: GPL-2.0
2195534Sscottl/*
3195534Sscottl * Copyright (C) 2016 InforceComputing
4195534Sscottl * Copyright (C) 2016 Linaro Ltd
5195534Sscottl * Copyright (C) 2023 BayLibre, SAS
6195534Sscottl *
7195534Sscottl * Authors:
8195534Sscottl * - Vinay Simha BN <simhavcs@gmail.com>
9195534Sscottl * - Sumit Semwal <sumit.semwal@linaro.org>
10195534Sscottl * - Guillaume La Roque <glaroque@baylibre.com>
11195534Sscottl *
12195534Sscottl */
13195534Sscottl
14195534Sscottl#include <linux/backlight.h>
15195534Sscottl#include <linux/delay.h>
16195534Sscottl#include <linux/gpio/consumer.h>
17195534Sscottl#include <linux/module.h>
18195534Sscottl#include <linux/of.h>
19195534Sscottl#include <linux/regulator/consumer.h>
20195534Sscottl
21195534Sscottl#include <video/mipi_display.h>
22195534Sscottl
23195534Sscottl#include <drm/drm_mipi_dsi.h>
24195534Sscottl#include <drm/drm_modes.h>
25195534Sscottl#include <drm/drm_panel.h>
26195534Sscottl
27195534Sscottl#define DSI_REG_MCAP	0xB0
28195534Sscottl#define DSI_REG_IS	0xB3 /* Interface Setting */
29195534Sscottl#define DSI_REG_IIS	0xB4 /* Interface ID Setting */
30195534Sscottl#define DSI_REG_CTRL	0xB6
31195534Sscottl
32195534Sscottlenum {
33195534Sscottl	IOVCC = 0,
34195534Sscottl	POWER = 1
35195534Sscottl};
36195534Sscottl
37195534Sscottlstruct stk_panel {
38195534Sscottl	const struct drm_display_mode *mode;
39195534Sscottl	struct backlight_device *backlight;
40195534Sscottl	struct drm_panel base;
41195534Sscottl	struct gpio_desc *enable_gpio; /* Power IC supply enable */
42195534Sscottl	struct gpio_desc *reset_gpio; /* External reset */
43195534Sscottl	struct mipi_dsi_device *dsi;
44195534Sscottl	struct regulator_bulk_data supplies[2];
45195534Sscottl};
46195534Sscottl
47195534Sscottlstatic inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
48195534Sscottl{
49195534Sscottl	return container_of(panel, struct stk_panel, base);
50195534Sscottl}
51195534Sscottl
52195534Sscottlstatic int stk_panel_init(struct stk_panel *stk)
53195534Sscottl{
54195534Sscottl	struct mipi_dsi_device *dsi = stk->dsi;
55195534Sscottl	struct device *dev = &stk->dsi->dev;
56195534Sscottl	int ret;
57195534Sscottl
58195534Sscottl	ret = mipi_dsi_dcs_soft_reset(dsi);
59195534Sscottl	if (ret < 0) {
60195534Sscottl		dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret);
61195534Sscottl		return ret;
62195534Sscottl	}
63195534Sscottl	mdelay(5);
64195534Sscottl
65195534Sscottl	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
66195534Sscottl	if (ret < 0) {
67195534Sscottl		dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
68195534Sscottl		return ret;
69195534Sscottl	}
70195534Sscottl	msleep(120);
71198849Smav
72198849Smav	mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04);
73198849Smav
74198849Smav	/* Interface setting, video mode */
75220616Smav	mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
76220616Smav	mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00);
77198849Smav	mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3);
78198849Smav
79198849Smav	ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77);
80201139Smav	if (ret < 0) {
81201139Smav		dev_err(dev, "failed to write display brightness: %d\n", ret);
82201139Smav		return ret;
83201139Smav	}
84201139Smav
85198849Smav	mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
86198849Smav			       MIPI_DCS_WRITE_MEMORY_START);
87198849Smav
88198849Smav	ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
89198849Smav	if (ret < 0) {
90198849Smav		dev_err(dev, "failed to set pixel format: %d\n", ret);
91198849Smav		return ret;
92198849Smav	}
93198849Smav
94198849Smav	ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1);
95198849Smav	if (ret < 0) {
96198849Smav		dev_err(dev, "failed to set column address: %d\n", ret);
97198849Smav		return ret;
98198849Smav	}
99198849Smav
100198849Smav	ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1);
101198849Smav	if (ret < 0) {
102198849Smav		dev_err(dev, "failed to set page address: %d\n", ret);
103200008Smav		return ret;
104200008Smav	}
105198849Smav
106198849Smav	return 0;
107198849Smav}
108198849Smav
109198849Smavstatic int stk_panel_on(struct stk_panel *stk)
110198849Smav{
111235897Smav	struct mipi_dsi_device *dsi = stk->dsi;
112235897Smav	struct device *dev = &stk->dsi->dev;
113235897Smav	int ret;
114235897Smav
115235897Smav	ret = mipi_dsi_dcs_set_display_on(dsi);
116235897Smav	if (ret < 0)
117235897Smav		dev_err(dev, "failed to set display on: %d\n", ret);
118235897Smav
119235897Smav	mdelay(20);
120235897Smav
121198849Smav	return ret;
122198849Smav}
123198849Smav
124198849Smavstatic void stk_panel_off(struct stk_panel *stk)
125198849Smav{
126198849Smav	struct mipi_dsi_device *dsi = stk->dsi;
127198849Smav	struct device *dev = &stk->dsi->dev;
128198849Smav	int ret;
129198849Smav
130198849Smav	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
131198849Smav
132198849Smav	ret = mipi_dsi_dcs_set_display_off(dsi);
133198849Smav	if (ret < 0)
134198849Smav		dev_err(dev, "failed to set display off: %d\n", ret);
135198849Smav
136198849Smav	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
137198849Smav	if (ret < 0)
138198849Smav		dev_err(dev, "failed to enter sleep mode: %d\n", ret);
139200008Smav
140198849Smav	msleep(100);
141198849Smav}
142198849Smav
143198849Smavstatic int stk_panel_unprepare(struct drm_panel *panel)
144198849Smav{
145198849Smav	struct stk_panel *stk = to_stk_panel(panel);
146198849Smav
147198849Smav	stk_panel_off(stk);
148198849Smav	regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
149198849Smav	gpiod_set_value(stk->reset_gpio, 0);
150198849Smav	gpiod_set_value(stk->enable_gpio, 1);
151198849Smav
152198849Smav	return 0;
153198849Smav}
154198849Smav
155198849Smavstatic int stk_panel_prepare(struct drm_panel *panel)
156198849Smav{
157198849Smav	struct stk_panel *stk = to_stk_panel(panel);
158198849Smav	struct device *dev = &stk->dsi->dev;
159198849Smav	int ret;
160198849Smav
161203421Smav	gpiod_set_value(stk->reset_gpio, 0);
162203421Smav	gpiod_set_value(stk->enable_gpio, 0);
163203421Smav	ret = regulator_enable(stk->supplies[IOVCC].consumer);
164220616Smav	if (ret < 0)
165220616Smav		return ret;
166198849Smav
167198849Smav	mdelay(8);
168198849Smav	ret = regulator_enable(stk->supplies[POWER].consumer);
169198849Smav	if (ret < 0)
170198849Smav		goto iovccoff;
171198849Smav
172198849Smav	mdelay(20);
173198849Smav	gpiod_set_value(stk->enable_gpio, 1);
174238363Sbrueffer	mdelay(20);
175238363Sbrueffer	gpiod_set_value(stk->reset_gpio, 1);
176198849Smav	mdelay(10);
177198849Smav	ret = stk_panel_init(stk);
178198849Smav	if (ret < 0) {
179198849Smav		dev_err(dev, "failed to init panel: %d\n", ret);
180198849Smav		goto poweroff;
181198849Smav	}
182198849Smav
183198849Smav	ret = stk_panel_on(stk);
184198849Smav	if (ret < 0) {
185198849Smav		dev_err(dev, "failed to set panel on: %d\n", ret);
186198849Smav		goto poweroff;
187198849Smav	}
188198849Smav
189198849Smav	return 0;
190198849Smav
191198849Smavpoweroff:
192198849Smav	regulator_disable(stk->supplies[POWER].consumer);
193198849Smaviovccoff:
194198849Smav	regulator_disable(stk->supplies[IOVCC].consumer);
195198849Smav	gpiod_set_value(stk->reset_gpio, 0);
196198849Smav	gpiod_set_value(stk->enable_gpio, 0);
197198849Smav
198198849Smav	return ret;
199198849Smav}
200198849Smav
201198849Smavstatic const struct drm_display_mode default_mode = {
202198849Smav		.clock = 163204,
203198849Smav		.hdisplay = 1200,
204198849Smav		.hsync_start = 1200 + 144,
205198849Smav		.hsync_end = 1200 + 144 + 16,
206198849Smav		.htotal = 1200 + 144 + 16 + 45,
207198849Smav		.vdisplay = 1920,
208198849Smav		.vsync_start = 1920 + 8,
209198849Smav		.vsync_end = 1920 + 8 + 4,
210198849Smav		.vtotal = 1920 + 8 + 4 + 4,
211198849Smav		.width_mm = 95,
212198849Smav		.height_mm = 151,
213198849Smav};
214198849Smav
215198849Smavstatic int stk_panel_get_modes(struct drm_panel *panel,
216198849Smav			       struct drm_connector *connector)
217198849Smav{
218203108Smav	struct drm_display_mode *mode;
219198849Smav
220198849Smav	mode = drm_mode_duplicate(connector->dev, &default_mode);
221198849Smav	if (!mode) {
222198849Smav		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
223198849Smav			default_mode.hdisplay, default_mode.vdisplay,
224198849Smav			drm_mode_vrefresh(&default_mode));
225198849Smav		return -ENOMEM;
226198849Smav	}
227198849Smav
228198849Smav	drm_mode_set_name(mode);
229198849Smav	drm_mode_probed_add(connector, mode);
230198849Smav	connector->display_info.width_mm = default_mode.width_mm;
231198849Smav	connector->display_info.height_mm = default_mode.height_mm;
232203108Smav	return 1;
233198849Smav}
234198849Smav
235198849Smavstatic int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
236198849Smav{
237198849Smav	struct mipi_dsi_device *dsi = bl_get_data(bl);
238198849Smav	int ret;
239198849Smav	u16 brightness;
240198849Smav
241198849Smav	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
242198849Smav	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
243203108Smav	if (ret < 0)
244198849Smav		return ret;
245198849Smav
246198849Smav	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
247198849Smav	return brightness & 0xff;
248198849Smav}
249198849Smav
250198849Smavstatic int dsi_dcs_bl_update_status(struct backlight_device *bl)
251198849Smav{
252198849Smav	struct mipi_dsi_device *dsi = bl_get_data(bl);
253198849Smav	struct device *dev = &dsi->dev;
254198849Smav	int ret;
255198849Smav
256198849Smav	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
257198849Smav	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
258198849Smav	if (ret < 0) {
259198849Smav		dev_err(dev, "failed to set DSI control: %d\n", ret);
260198849Smav		return ret;
261198849Smav	}
262198849Smav
263198849Smav	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
264198849Smav	return 0;
265198849Smav}
266198849Smav
267198849Smavstatic const struct backlight_ops dsi_bl_ops = {
268198849Smav	.update_status = dsi_dcs_bl_update_status,
269198849Smav	.get_brightness = dsi_dcs_bl_get_brightness,
270198849Smav};
271198849Smav
272195534Sscottlstatic struct backlight_device *
273195534Sscottldrm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
274195534Sscottl{
275195534Sscottl	struct device *dev = &dsi->dev;
276195534Sscottl	struct backlight_properties props = {
277195534Sscottl		.type = BACKLIGHT_RAW,
278195534Sscottl		.brightness = 255,
279195534Sscottl		.max_brightness = 255,
280195534Sscottl	};
281200218Smav
282200218Smav	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
283223019Smav					      &dsi_bl_ops, &props);
284200218Smav}
285200218Smav
286195534Sscottlstatic const struct drm_panel_funcs stk_panel_funcs = {
287197541Smav	.unprepare = stk_panel_unprepare,
288197541Smav	.prepare = stk_panel_prepare,
289197541Smav	.get_modes = stk_panel_get_modes,
290195534Sscottl};
291195534Sscottl
292195534Sscottlstatic const struct of_device_id stk_of_match[] = {
293195534Sscottl	{ .compatible = "startek,kd070fhfid015", },
294195534Sscottl	{ }
295195534Sscottl};
296195534SscottlMODULE_DEVICE_TABLE(of, stk_of_match);
297195534Sscottl
298195534Sscottlstatic int stk_panel_add(struct stk_panel *stk)
299235897Smav{
300257049Smav	struct device *dev = &stk->dsi->dev;
301257049Smav	int ret;
302257049Smav
303257049Smav	stk->mode = &default_mode;
304257049Smav
305257049Smav	stk->supplies[IOVCC].supply = "iovcc";
306257049Smav	stk->supplies[POWER].supply = "power";
307257049Smav	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
308257049Smav	if (ret) {
309257049Smav		dev_err(dev, "regulator_bulk failed\n");
310257049Smav		return ret;
311257049Smav	}
312235897Smav
313235897Smav	stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
314235897Smav	if (IS_ERR(stk->reset_gpio)) {
315235897Smav		ret = PTR_ERR(stk->reset_gpio);
316235897Smav		dev_err(dev, "cannot get reset-gpios %d\n", ret);
317235897Smav		return ret;
318235897Smav	}
319235897Smav
320235897Smav	stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
321235897Smav	if (IS_ERR(stk->enable_gpio)) {
322235897Smav		ret = PTR_ERR(stk->enable_gpio);
323235897Smav		dev_err(dev, "cannot get enable-gpio %d\n", ret);
324235897Smav		return ret;
325235897Smav	}
326257049Smav
327257049Smav	stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
328257049Smav	if (IS_ERR(stk->backlight)) {
329257049Smav		ret = PTR_ERR(stk->backlight);
330257049Smav		dev_err(dev, "failed to register backlight %d\n", ret);
331257049Smav		return ret;
332257049Smav	}
333257049Smav
334257049Smav	drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
335257049Smav		       DRM_MODE_CONNECTOR_DSI);
336257049Smav
337257049Smav	drm_panel_add(&stk->base);
338198897Smav
339198897Smav	return 0;
340198897Smav}
341198897Smav
342198897Smavstatic int stk_panel_probe(struct mipi_dsi_device *dsi)
343198897Smav{
344198897Smav	struct stk_panel *stk;
345198897Smav	int ret;
346198897Smav
347198897Smav	dsi->lanes = 4;
348198897Smav	dsi->format = MIPI_DSI_FMT_RGB888;
349198897Smav	dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
350198897Smav
351198897Smav	stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
352198897Smav	if (!stk)
353198897Smav		return -ENOMEM;
354198897Smav
355198897Smav	mipi_dsi_set_drvdata(dsi, stk);
356198897Smav
357198897Smav	stk->dsi = dsi;
358198897Smav
359198897Smav	ret = stk_panel_add(stk);
360198897Smav	if (ret < 0)
361198897Smav		return ret;
362198897Smav
363198897Smav	ret = mipi_dsi_attach(dsi);
364198897Smav	if (ret < 0)
365198897Smav		drm_panel_remove(&stk->base);
366198897Smav
367198897Smav	return 0;
368198897Smav}
369198897Smav
370195534Sscottlstatic void stk_panel_remove(struct mipi_dsi_device *dsi)
371196659Smav{
372195534Sscottl	struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
373195534Sscottl	int err;
374195534Sscottl
375195534Sscottl	err = mipi_dsi_detach(dsi);
376200008Smav	if (err < 0)
377200008Smav		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
378200008Smav			err);
379200008Smav
380200008Smav	drm_panel_remove(&stk->base);
381195534Sscottl}
382195534Sscottl
383195534Sscottlstatic struct mipi_dsi_driver stk_panel_driver = {
384195534Sscottl	.driver = {
385195534Sscottl		.name = "panel-startek-kd070fhfid015",
386238393Sbrueffer		.of_match_table = stk_of_match,
387195534Sscottl	},
388195534Sscottl	.probe = stk_panel_probe,
389195534Sscottl	.remove = stk_panel_remove,
390195534Sscottl};
391195534Sscottlmodule_mipi_dsi_driver(stk_panel_driver);
392195534Sscottl
393195534SscottlMODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
394248695SmavMODULE_DESCRIPTION("STARTEK KD070FHFID015");
395195534SscottlMODULE_LICENSE("GPL");
396200008Smav