xref: /linux/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c (revision de848da12f752170c2ebe114804a985314fd5a6a)
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 
32 enum {
33 	IOVCC = 0,
34 	POWER = 1
35 };
36 
37 struct stk_panel {
38 	const struct drm_display_mode *mode;
39 	struct backlight_device *backlight;
40 	struct drm_panel base;
41 	struct gpio_desc *enable_gpio; /* Power IC supply enable */
42 	struct gpio_desc *reset_gpio; /* External reset */
43 	struct mipi_dsi_device *dsi;
44 	struct regulator_bulk_data supplies[2];
45 };
46 
47 static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
48 {
49 	return container_of(panel, struct stk_panel, base);
50 }
51 
52 static int stk_panel_init(struct stk_panel *stk)
53 {
54 	struct mipi_dsi_device *dsi = stk->dsi;
55 	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
56 
57 	mipi_dsi_dcs_soft_reset_multi(&dsi_ctx);
58 	mipi_dsi_msleep(&dsi_ctx, 5);
59 	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
60 	mipi_dsi_msleep(&dsi_ctx, 120);
61 
62 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_MCAP, 0x04);
63 
64 	/* Interface setting, video mode */
65 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
66 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IIS, 0x0c, 0x00);
67 	mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_CTRL, 0x3a, 0xd3);
68 
69 	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x77);
70 
71 	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
72 				     MIPI_DCS_WRITE_MEMORY_START);
73 
74 	mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, 0x77);
75 	mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, stk->mode->hdisplay - 1);
76 	mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, stk->mode->vdisplay - 1);
77 
78 	return dsi_ctx.accum_err;
79 }
80 
81 static int stk_panel_on(struct stk_panel *stk)
82 {
83 	struct mipi_dsi_device *dsi = stk->dsi;
84 	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
85 
86 	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
87 
88 	mipi_dsi_msleep(&dsi_ctx, 20);
89 
90 	return dsi_ctx.accum_err;
91 }
92 
93 static void stk_panel_off(struct stk_panel *stk)
94 {
95 	struct mipi_dsi_device *dsi = stk->dsi;
96 	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
97 
98 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
99 
100 	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
101 	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
102 
103 	mipi_dsi_msleep(&dsi_ctx, 100);
104 }
105 
106 static int stk_panel_unprepare(struct drm_panel *panel)
107 {
108 	struct stk_panel *stk = to_stk_panel(panel);
109 
110 	stk_panel_off(stk);
111 	regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
112 	gpiod_set_value(stk->reset_gpio, 0);
113 	gpiod_set_value(stk->enable_gpio, 1);
114 
115 	return 0;
116 }
117 
118 static int stk_panel_prepare(struct drm_panel *panel)
119 {
120 	struct stk_panel *stk = to_stk_panel(panel);
121 	int ret;
122 
123 	gpiod_set_value(stk->reset_gpio, 0);
124 	gpiod_set_value(stk->enable_gpio, 0);
125 	ret = regulator_enable(stk->supplies[IOVCC].consumer);
126 	if (ret < 0)
127 		return ret;
128 
129 	mdelay(8);
130 	ret = regulator_enable(stk->supplies[POWER].consumer);
131 	if (ret < 0)
132 		goto iovccoff;
133 
134 	mdelay(20);
135 	gpiod_set_value(stk->enable_gpio, 1);
136 	mdelay(20);
137 	gpiod_set_value(stk->reset_gpio, 1);
138 	mdelay(10);
139 	ret = stk_panel_init(stk);
140 	if (ret < 0)
141 		goto poweroff;
142 
143 	ret = stk_panel_on(stk);
144 	if (ret < 0)
145 		goto poweroff;
146 
147 	return 0;
148 
149 poweroff:
150 	regulator_disable(stk->supplies[POWER].consumer);
151 iovccoff:
152 	regulator_disable(stk->supplies[IOVCC].consumer);
153 	gpiod_set_value(stk->reset_gpio, 0);
154 	gpiod_set_value(stk->enable_gpio, 0);
155 
156 	return ret;
157 }
158 
159 static const struct drm_display_mode default_mode = {
160 		.clock = 163204,
161 		.hdisplay = 1200,
162 		.hsync_start = 1200 + 144,
163 		.hsync_end = 1200 + 144 + 16,
164 		.htotal = 1200 + 144 + 16 + 45,
165 		.vdisplay = 1920,
166 		.vsync_start = 1920 + 8,
167 		.vsync_end = 1920 + 8 + 4,
168 		.vtotal = 1920 + 8 + 4 + 4,
169 		.width_mm = 95,
170 		.height_mm = 151,
171 };
172 
173 static int stk_panel_get_modes(struct drm_panel *panel,
174 			       struct drm_connector *connector)
175 {
176 	struct drm_display_mode *mode;
177 
178 	mode = drm_mode_duplicate(connector->dev, &default_mode);
179 	if (!mode) {
180 		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
181 			default_mode.hdisplay, default_mode.vdisplay,
182 			drm_mode_vrefresh(&default_mode));
183 		return -ENOMEM;
184 	}
185 
186 	drm_mode_set_name(mode);
187 	drm_mode_probed_add(connector, mode);
188 	connector->display_info.width_mm = default_mode.width_mm;
189 	connector->display_info.height_mm = default_mode.height_mm;
190 	return 1;
191 }
192 
193 static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
194 {
195 	struct mipi_dsi_device *dsi = bl_get_data(bl);
196 	int ret;
197 	u16 brightness;
198 
199 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
200 	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
201 	if (ret < 0)
202 		return ret;
203 
204 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
205 	return brightness & 0xff;
206 }
207 
208 static int dsi_dcs_bl_update_status(struct backlight_device *bl)
209 {
210 	struct mipi_dsi_device *dsi = bl_get_data(bl);
211 	struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
212 
213 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
214 	mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, bl->props.brightness);
215 	if (dsi_ctx.accum_err)
216 		return dsi_ctx.accum_err;
217 
218 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
219 	return dsi_ctx.accum_err;
220 }
221 
222 static const struct backlight_ops dsi_bl_ops = {
223 	.update_status = dsi_dcs_bl_update_status,
224 	.get_brightness = dsi_dcs_bl_get_brightness,
225 };
226 
227 static struct backlight_device *
228 drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
229 {
230 	struct device *dev = &dsi->dev;
231 	struct backlight_properties props = {
232 		.type = BACKLIGHT_RAW,
233 		.brightness = 255,
234 		.max_brightness = 255,
235 	};
236 
237 	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
238 					      &dsi_bl_ops, &props);
239 }
240 
241 static const struct drm_panel_funcs stk_panel_funcs = {
242 	.unprepare = stk_panel_unprepare,
243 	.prepare = stk_panel_prepare,
244 	.get_modes = stk_panel_get_modes,
245 };
246 
247 static const struct of_device_id stk_of_match[] = {
248 	{ .compatible = "startek,kd070fhfid015", },
249 	{ }
250 };
251 MODULE_DEVICE_TABLE(of, stk_of_match);
252 
253 static int stk_panel_add(struct stk_panel *stk)
254 {
255 	struct device *dev = &stk->dsi->dev;
256 	int ret;
257 
258 	stk->mode = &default_mode;
259 
260 	stk->supplies[IOVCC].supply = "iovcc";
261 	stk->supplies[POWER].supply = "power";
262 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
263 	if (ret) {
264 		dev_err(dev, "regulator_bulk failed\n");
265 		return ret;
266 	}
267 
268 	stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
269 	if (IS_ERR(stk->reset_gpio)) {
270 		ret = PTR_ERR(stk->reset_gpio);
271 		dev_err(dev, "cannot get reset-gpios %d\n", ret);
272 		return ret;
273 	}
274 
275 	stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
276 	if (IS_ERR(stk->enable_gpio)) {
277 		ret = PTR_ERR(stk->enable_gpio);
278 		dev_err(dev, "cannot get enable-gpio %d\n", ret);
279 		return ret;
280 	}
281 
282 	stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
283 	if (IS_ERR(stk->backlight)) {
284 		ret = PTR_ERR(stk->backlight);
285 		dev_err(dev, "failed to register backlight %d\n", ret);
286 		return ret;
287 	}
288 
289 	drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
290 		       DRM_MODE_CONNECTOR_DSI);
291 
292 	drm_panel_add(&stk->base);
293 
294 	return 0;
295 }
296 
297 static int stk_panel_probe(struct mipi_dsi_device *dsi)
298 {
299 	struct stk_panel *stk;
300 	int ret;
301 
302 	dsi->lanes = 4;
303 	dsi->format = MIPI_DSI_FMT_RGB888;
304 	dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
305 
306 	stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
307 	if (!stk)
308 		return -ENOMEM;
309 
310 	mipi_dsi_set_drvdata(dsi, stk);
311 
312 	stk->dsi = dsi;
313 
314 	ret = stk_panel_add(stk);
315 	if (ret < 0)
316 		return ret;
317 
318 	ret = mipi_dsi_attach(dsi);
319 	if (ret < 0)
320 		drm_panel_remove(&stk->base);
321 
322 	return 0;
323 }
324 
325 static void stk_panel_remove(struct mipi_dsi_device *dsi)
326 {
327 	struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
328 	int err;
329 
330 	err = mipi_dsi_detach(dsi);
331 	if (err < 0)
332 		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
333 			err);
334 
335 	drm_panel_remove(&stk->base);
336 }
337 
338 static struct mipi_dsi_driver stk_panel_driver = {
339 	.driver = {
340 		.name = "panel-startek-kd070fhfid015",
341 		.of_match_table = stk_of_match,
342 	},
343 	.probe = stk_panel_probe,
344 	.remove = stk_panel_remove,
345 };
346 module_mipi_dsi_driver(stk_panel_driver);
347 
348 MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
349 MODULE_DESCRIPTION("STARTEK KD070FHFID015");
350 MODULE_LICENSE("GPL");
351