// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
struct kingdisplay_panel {
struct drm_panel base;
struct mipi_dsi_device *link;
struct regulator *supply;
struct gpio_desc *enable_gpio;
};
struct kingdisplay_panel_cmd {
char cmd;
char data;
};
/*
* According to the discussion on
* https://review.coreboot.org/#/c/coreboot/+/22472/
* the panel init array is not part of the panels datasheet but instead
* just came in this form from the panel vendor.
*/
static const struct kingdisplay_panel_cmd init_code[] = {
/* voltage setting */
{
0 xB0,
0 x00 },
{
0 xB2,
0 x02 },
{
0 xB3,
0 x11 },
{
0 xB4,
0 x00 },
{
0 xB6,
0 x80 },
/* VCOM disable */
{
0 xB7,
0 x02 },
{
0 xB8,
0 x80 },
{
0 xBA,
0 x43 },
/* VCOM setting */
{
0 xBB,
0 x53 },
/* VSP setting */
{
0 xBC,
0 x0A },
/* VSN setting */
{
0 xBD,
0 x4A },
/* VGH setting */
{
0 xBE,
0 x2F },
/* VGL setting */
{
0 xBF,
0 x1A },
{
0 xF0,
0 x39 },
{
0 xF1,
0 x22 },
/* Gamma setting */
{
0 xB0,
0 x02 },
{
0 xC0,
0 x00 },
{
0 xC1,
0 x01 },
{
0 xC2,
0 x0B },
{
0 xC3,
0 x15 },
{
0 xC4,
0 x22 },
{
0 xC5,
0 x11 },
{
0 xC6,
0 x15 },
{
0 xC7,
0 x19 },
{
0 xC8,
0 x1A },
{
0 xC9,
0 x16 },
{
0 xCA,
0 x18 },
{
0 xCB,
0 x13 },
{
0 xCC,
0 x18 },
{
0 xCD,
0 x13 },
{
0 xCE,
0 x1C },
{
0 xCF,
0 x19 },
{
0 xD0,
0 x21 },
{
0 xD1,
0 x2C },
{
0 xD2,
0 x2F },
{
0 xD3,
0 x30 },
{
0 xD4,
0 x19 },
{
0 xD5,
0 x1F },
{
0 xD6,
0 x00 },
{
0 xD7,
0 x01 },
{
0 xD8,
0 x0B },
{
0 xD9,
0 x15 },
{
0 xDA,
0 x22 },
{
0 xDB,
0 x11 },
{
0 xDC,
0 x15 },
{
0 xDD,
0 x19 },
{
0 xDE,
0 x1A },
{
0 xDF,
0 x16 },
{
0 xE0,
0 x18 },
{
0 xE1,
0 x13 },
{
0 xE2,
0 x18 },
{
0 xE3,
0 x13 },
{
0 xE4,
0 x1C },
{
0 xE5,
0 x19 },
{
0 xE6,
0 x21 },
{
0 xE7,
0 x2C },
{
0 xE8,
0 x2F },
{
0 xE9,
0 x30 },
{
0 xEA,
0 x19 },
{
0 xEB,
0 x1F },
/* GOA MUX setting */
{
0 xB0,
0 x01 },
{
0 xC0,
0 x10 },
{
0 xC1,
0 x0F },
{
0 xC2,
0 x0E },
{
0 xC3,
0 x0D },
{
0 xC4,
0 x0C },
{
0 xC5,
0 x0B },
{
0 xC6,
0 x0A },
{
0 xC7,
0 x09 },
{
0 xC8,
0 x08 },
{
0 xC9,
0 x07 },
{
0 xCA,
0 x06 },
{
0 xCB,
0 x05 },
{
0 xCC,
0 x00 },
{
0 xCD,
0 x01 },
{
0 xCE,
0 x02 },
{
0 xCF,
0 x03 },
{
0 xD0,
0 x04 },
{
0 xD6,
0 x10 },
{
0 xD7,
0 x0F },
{
0 xD8,
0 x0E },
{
0 xD9,
0 x0D },
{
0 xDA,
0 x0C },
{
0 xDB,
0 x0B },
{
0 xDC,
0 x0A },
{
0 xDD,
0 x09 },
{
0 xDE,
0 x08 },
{
0 xDF,
0 x07 },
{
0 xE0,
0 x06 },
{
0 xE1,
0 x05 },
{
0 xE2,
0 x00 },
{
0 xE3,
0 x01 },
{
0 xE4,
0 x02 },
{
0 xE5,
0 x03 },
{
0 xE6,
0 x04 },
{
0 xE7,
0 x00 },
{
0 xEC,
0 xC0 },
/* GOA timing setting */
{
0 xB0,
0 x03 },
{
0 xC0,
0 x01 },
{
0 xC2,
0 x6F },
{
0 xC3,
0 x6F },
{
0 xC5,
0 x36 },
{
0 xC8,
0 x08 },
{
0 xC9,
0 x04 },
{
0 xCA,
0 x41 },
{
0 xCC,
0 x43 },
{
0 xCF,
0 x60 },
{
0 xD2,
0 x04 },
{
0 xD3,
0 x04 },
{
0 xD4,
0 x03 },
{
0 xD5,
0 x02 },
{
0 xD6,
0 x01 },
{
0 xD7,
0 x00 },
{
0 xDB,
0 x01 },
{
0 xDE,
0 x36 },
{
0 xE6,
0 x6F },
{
0 xE7,
0 x6F },
/* GOE setting */
{
0 xB0,
0 x06 },
{
0 xB8,
0 xA5 },
{
0 xC0,
0 xA5 },
{
0 xD5,
0 x3F },
};
static inline
struct kingdisplay_panel *to_kingdisplay_panel(
struct drm_panel *panel)
{
return container_of(panel,
struct kingdisplay_panel, base);
}
static int kingdisplay_panel_disable(
struct drm_panel *panel)
{
struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
int err;
err = mipi_dsi_dcs_set_display_off(kingdisplay->link);
if (err <
0 )
dev_err(panel->dev,
"failed to set display off: %d\n" , err);
return 0 ;
}
static int kingdisplay_panel_unprepare(
struct drm_panel *panel)
{
struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
int err;
err = mipi_dsi_dcs_enter_sleep_mode(kingdisplay->link);
if (err <
0 ) {
dev_err(panel->dev,
"failed to enter sleep mode: %d\n" , err);
return err;
}
/* T15: 120ms */
msleep(
120 );
gpiod_set_value_cansleep(kingdisplay->enable_gpio,
0 );
err = regulator_disable(kingdisplay->supply);
if (err <
0 )
return err;
return 0 ;
}
static int kingdisplay_panel_prepare(
struct drm_panel *panel)
{
struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
int err, regulator_err;
unsigned int i;
gpiod_set_value_cansleep(kingdisplay->enable_gpio,
0 );
err = regulator_enable(kingdisplay->supply);
if (err <
0 )
return err;
/* T2: 15ms */
usleep_range(
15000 ,
16000 );
gpiod_set_value_cansleep(kingdisplay->enable_gpio,
1 );
/* T4: 15ms */
usleep_range(
15000 ,
16000 );
for (i =
0 ; i < ARRAY_SIZE(init_code); i++) {
err = mipi_dsi_generic_write(kingdisplay->link, &init_code[i],
sizeof (
struct kingdisplay_panel_cmd));
if (err <
0 ) {
dev_err(panel->dev,
"failed write init cmds: %d\n" , err);
goto poweroff;
}
}
err = mipi_dsi_dcs_exit_sleep_mode(kingdisplay->link);
if (err <
0 ) {
dev_err(panel->dev,
"failed to exit sleep mode: %d\n" , err);
goto poweroff;
}
/* T6: 120ms */
msleep(
120 );
err = mipi_dsi_dcs_set_display_on(kingdisplay->link);
if (err <
0 ) {
dev_err(panel->dev,
"failed to set display on: %d\n" , err);
goto poweroff;
}
/* T7: 10ms */
usleep_range(
10000 ,
11000 );
return 0 ;
poweroff:
gpiod_set_value_cansleep(kingdisplay->enable_gpio,
0 );
regulator_err = regulator_disable(kingdisplay->supply);
if (regulator_err)
dev_err(panel->dev,
"failed to disable regulator: %d\n" , regulator_err);
return err;
}
static const struct drm_display_mode default_mode = {
.clock =
229000 ,
.hdisplay =
1536 ,
.hsync_start =
1536 +
100 ,
.hsync_end =
1536 +
100 +
24 ,
.htotal =
1536 +
100 +
24 +
100 ,
.vdisplay =
2048 ,
.vsync_start =
2048 +
95 ,
.vsync_end =
2048 +
95 +
2 ,
.vtotal =
2048 +
95 +
2 +
23 ,
};
static int kingdisplay_panel_get_modes(
struct drm_panel *panel,
struct drm_connector *connector)
{
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
dev_err(panel->dev,
"failed to add mode %ux%u@%u\n" ,
default_mode.hdisplay, default_mode.vdisplay,
drm_mode_vrefresh(&default_mode));
return -ENOMEM;
}
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
connector->display_info.width_mm =
147 ;
connector->display_info.height_mm =
196 ;
connector->display_info.bpc =
8 ;
return 1 ;
}
static const struct drm_panel_funcs kingdisplay_panel_funcs = {
.disable = kingdisplay_panel_disable,
.unprepare = kingdisplay_panel_unprepare,
.prepare = kingdisplay_panel_prepare,
.get_modes = kingdisplay_panel_get_modes,
};
static const struct of_device_id kingdisplay_of_match[] = {
{ .compatible =
"kingdisplay,kd097d04" , },
{ }
};
MODULE_DEVICE_TABLE(of, kingdisplay_of_match);
static int kingdisplay_panel_add(
struct kingdisplay_panel *kingdisplay)
{
struct device *dev = &kingdisplay->link->dev;
int err;
kingdisplay->supply = devm_regulator_get(dev,
"power" );
if (IS_ERR(kingdisplay->supply))
return PTR_ERR(kingdisplay->supply);
kingdisplay->enable_gpio = devm_gpiod_get_optional(dev,
"enable" ,
GPIOD_OUT_HIGH);
if (IS_ERR(kingdisplay->enable_gpio)) {
err = PTR_ERR(kingdisplay->enable_gpio);
dev_dbg(dev,
"failed to get enable gpio: %d\n" , err);
kingdisplay->enable_gpio = NULL;
}
err = drm_panel_of_backlight(&kingdisplay->base);
if (err)
return err;
drm_panel_add(&kingdisplay->base);
return 0 ;
}
static void kingdisplay_panel_del(
struct kingdisplay_panel *kingdisplay)
{
drm_panel_remove(&kingdisplay->base);
}
static int kingdisplay_panel_probe(
struct mipi_dsi_device *dsi)
{
struct kingdisplay_panel *kingdisplay;
int err;
dsi->lanes =
4 ;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
kingdisplay = devm_drm_panel_alloc(&dsi->dev, __typeof(*kingdisplay), base,
&kingdisplay_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(kingdisplay))
return PTR_ERR(kingdisplay);
mipi_dsi_set_drvdata(dsi, kingdisplay);
kingdisplay->link = dsi;
err = kingdisplay_panel_add(kingdisplay);
if (err <
0 )
return err;
err = mipi_dsi_attach(dsi);
if (err <
0 ) {
kingdisplay_panel_del(kingdisplay);
return err;
}
return 0 ;
}
static void kingdisplay_panel_remove(
struct mipi_dsi_device *dsi)
{
struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
int err;
err = mipi_dsi_detach(dsi);
if (err <
0 )
dev_err(&dsi->dev,
"failed to detach from DSI host: %d\n" , err);
kingdisplay_panel_del(kingdisplay);
}
static struct mipi_dsi_driver kingdisplay_panel_driver = {
.driver = {
.name =
"panel-kingdisplay-kd097d04" ,
.of_match_table = kingdisplay_of_match,
},
.probe = kingdisplay_panel_probe,
.remove = kingdisplay_panel_remove,
};
module_mipi_dsi_driver(kingdisplay_panel_driver);
MODULE_AUTHOR(
"Chris Zhong <zyw@rock-chips.com>" );
MODULE_AUTHOR(
"Nickey Yang <nickey.yang@rock-chips.com>" );
MODULE_DESCRIPTION(
"kingdisplay KD097D04 panel driver" );
MODULE_LICENSE(
"GPL v2" );
Messung V0.5 in Prozent C=94 H=97 G=95
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland