1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
6 #include <linux/delay.h>
7 #include <linux/gpio/consumer.h>
8 #include <linux/module.h>
10 #include <linux/regulator/consumer.h>
12 #include <video/mipi_display.h>
14 #include <drm/drm_crtc.h>
15 #include <drm/drm_device.h>
16 #include <drm/drm_mipi_dsi.h>
17 #include <drm/drm_modes.h>
18 #include <drm/drm_panel.h>
20 struct kingdisplay_panel
{
21 struct drm_panel base
;
22 struct mipi_dsi_device
*link
;
24 struct regulator
*supply
;
25 struct gpio_desc
*enable_gpio
;
31 struct kingdisplay_panel_cmd
{
37 * According to the discussion on
38 * https://review.coreboot.org/#/c/coreboot/+/22472/
39 * the panel init array is not part of the panels datasheet but instead
40 * just came in this form from the panel vendor.
42 static const struct kingdisplay_panel_cmd init_code
[] = {
111 /* GOA MUX setting */
149 /* GOA timing setting */
178 struct kingdisplay_panel
*to_kingdisplay_panel(struct drm_panel
*panel
)
180 return container_of(panel
, struct kingdisplay_panel
, base
);
183 static int kingdisplay_panel_disable(struct drm_panel
*panel
)
185 struct kingdisplay_panel
*kingdisplay
= to_kingdisplay_panel(panel
);
188 if (!kingdisplay
->enabled
)
191 err
= mipi_dsi_dcs_set_display_off(kingdisplay
->link
);
193 dev_err(panel
->dev
, "failed to set display off: %d\n", err
);
195 kingdisplay
->enabled
= false;
200 static int kingdisplay_panel_unprepare(struct drm_panel
*panel
)
202 struct kingdisplay_panel
*kingdisplay
= to_kingdisplay_panel(panel
);
205 if (!kingdisplay
->prepared
)
208 err
= mipi_dsi_dcs_enter_sleep_mode(kingdisplay
->link
);
210 dev_err(panel
->dev
, "failed to enter sleep mode: %d\n", err
);
217 gpiod_set_value_cansleep(kingdisplay
->enable_gpio
, 0);
219 err
= regulator_disable(kingdisplay
->supply
);
223 kingdisplay
->prepared
= false;
228 static int kingdisplay_panel_prepare(struct drm_panel
*panel
)
230 struct kingdisplay_panel
*kingdisplay
= to_kingdisplay_panel(panel
);
231 int err
, regulator_err
;
234 if (kingdisplay
->prepared
)
237 gpiod_set_value_cansleep(kingdisplay
->enable_gpio
, 0);
239 err
= regulator_enable(kingdisplay
->supply
);
244 usleep_range(15000, 16000);
246 gpiod_set_value_cansleep(kingdisplay
->enable_gpio
, 1);
249 usleep_range(15000, 16000);
251 for (i
= 0; i
< ARRAY_SIZE(init_code
); i
++) {
252 err
= mipi_dsi_generic_write(kingdisplay
->link
, &init_code
[i
],
253 sizeof(struct kingdisplay_panel_cmd
));
255 dev_err(panel
->dev
, "failed write init cmds: %d\n", err
);
260 err
= mipi_dsi_dcs_exit_sleep_mode(kingdisplay
->link
);
262 dev_err(panel
->dev
, "failed to exit sleep mode: %d\n", err
);
269 err
= mipi_dsi_dcs_set_display_on(kingdisplay
->link
);
271 dev_err(panel
->dev
, "failed to set display on: %d\n", err
);
276 usleep_range(10000, 11000);
278 kingdisplay
->prepared
= true;
283 gpiod_set_value_cansleep(kingdisplay
->enable_gpio
, 0);
285 regulator_err
= regulator_disable(kingdisplay
->supply
);
287 dev_err(panel
->dev
, "failed to disable regulator: %d\n", regulator_err
);
292 static int kingdisplay_panel_enable(struct drm_panel
*panel
)
294 struct kingdisplay_panel
*kingdisplay
= to_kingdisplay_panel(panel
);
296 if (kingdisplay
->enabled
)
299 kingdisplay
->enabled
= true;
304 static const struct drm_display_mode default_mode
= {
307 .hsync_start
= 1536 + 100,
308 .hsync_end
= 1536 + 100 + 24,
309 .htotal
= 1536 + 100 + 24 + 100,
311 .vsync_start
= 2048 + 95,
312 .vsync_end
= 2048 + 95 + 2,
313 .vtotal
= 2048 + 95 + 2 + 23,
316 static int kingdisplay_panel_get_modes(struct drm_panel
*panel
,
317 struct drm_connector
*connector
)
319 struct drm_display_mode
*mode
;
321 mode
= drm_mode_duplicate(connector
->dev
, &default_mode
);
323 dev_err(panel
->dev
, "failed to add mode %ux%u@%u\n",
324 default_mode
.hdisplay
, default_mode
.vdisplay
,
325 drm_mode_vrefresh(&default_mode
));
329 drm_mode_set_name(mode
);
331 drm_mode_probed_add(connector
, mode
);
333 connector
->display_info
.width_mm
= 147;
334 connector
->display_info
.height_mm
= 196;
335 connector
->display_info
.bpc
= 8;
340 static const struct drm_panel_funcs kingdisplay_panel_funcs
= {
341 .disable
= kingdisplay_panel_disable
,
342 .unprepare
= kingdisplay_panel_unprepare
,
343 .prepare
= kingdisplay_panel_prepare
,
344 .enable
= kingdisplay_panel_enable
,
345 .get_modes
= kingdisplay_panel_get_modes
,
348 static const struct of_device_id kingdisplay_of_match
[] = {
349 { .compatible
= "kingdisplay,kd097d04", },
352 MODULE_DEVICE_TABLE(of
, kingdisplay_of_match
);
354 static int kingdisplay_panel_add(struct kingdisplay_panel
*kingdisplay
)
356 struct device
*dev
= &kingdisplay
->link
->dev
;
359 kingdisplay
->supply
= devm_regulator_get(dev
, "power");
360 if (IS_ERR(kingdisplay
->supply
))
361 return PTR_ERR(kingdisplay
->supply
);
363 kingdisplay
->enable_gpio
= devm_gpiod_get_optional(dev
, "enable",
365 if (IS_ERR(kingdisplay
->enable_gpio
)) {
366 err
= PTR_ERR(kingdisplay
->enable_gpio
);
367 dev_dbg(dev
, "failed to get enable gpio: %d\n", err
);
368 kingdisplay
->enable_gpio
= NULL
;
371 drm_panel_init(&kingdisplay
->base
, &kingdisplay
->link
->dev
,
372 &kingdisplay_panel_funcs
, DRM_MODE_CONNECTOR_DSI
);
374 err
= drm_panel_of_backlight(&kingdisplay
->base
);
378 drm_panel_add(&kingdisplay
->base
);
383 static void kingdisplay_panel_del(struct kingdisplay_panel
*kingdisplay
)
385 drm_panel_remove(&kingdisplay
->base
);
388 static int kingdisplay_panel_probe(struct mipi_dsi_device
*dsi
)
390 struct kingdisplay_panel
*kingdisplay
;
394 dsi
->format
= MIPI_DSI_FMT_RGB888
;
395 dsi
->mode_flags
= MIPI_DSI_MODE_VIDEO
| MIPI_DSI_MODE_VIDEO_BURST
|
398 kingdisplay
= devm_kzalloc(&dsi
->dev
, sizeof(*kingdisplay
), GFP_KERNEL
);
402 mipi_dsi_set_drvdata(dsi
, kingdisplay
);
403 kingdisplay
->link
= dsi
;
405 err
= kingdisplay_panel_add(kingdisplay
);
409 return mipi_dsi_attach(dsi
);
412 static int kingdisplay_panel_remove(struct mipi_dsi_device
*dsi
)
414 struct kingdisplay_panel
*kingdisplay
= mipi_dsi_get_drvdata(dsi
);
417 err
= drm_panel_unprepare(&kingdisplay
->base
);
419 dev_err(&dsi
->dev
, "failed to unprepare panel: %d\n", err
);
421 err
= drm_panel_disable(&kingdisplay
->base
);
423 dev_err(&dsi
->dev
, "failed to disable panel: %d\n", err
);
425 err
= mipi_dsi_detach(dsi
);
427 dev_err(&dsi
->dev
, "failed to detach from DSI host: %d\n", err
);
429 kingdisplay_panel_del(kingdisplay
);
434 static void kingdisplay_panel_shutdown(struct mipi_dsi_device
*dsi
)
436 struct kingdisplay_panel
*kingdisplay
= mipi_dsi_get_drvdata(dsi
);
438 drm_panel_unprepare(&kingdisplay
->base
);
439 drm_panel_disable(&kingdisplay
->base
);
442 static struct mipi_dsi_driver kingdisplay_panel_driver
= {
444 .name
= "panel-kingdisplay-kd097d04",
445 .of_match_table
= kingdisplay_of_match
,
447 .probe
= kingdisplay_panel_probe
,
448 .remove
= kingdisplay_panel_remove
,
449 .shutdown
= kingdisplay_panel_shutdown
,
451 module_mipi_dsi_driver(kingdisplay_panel_driver
);
453 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
454 MODULE_AUTHOR("Nickey Yang <nickey.yang@rock-chips.com>");
455 MODULE_DESCRIPTION("kingdisplay KD097D04 panel driver");
456 MODULE_LICENSE("GPL v2");