linux/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c
Uwe Kleine-König 79abca2b39 drm/mipi-dsi: Make remove callback return void
All implementations return 0 and the return value of mipi_dsi_drv_remove()
is ignored anyhow.

So change the prototype of the remove function to return no value. This
way driver authors are not tempted to assume that passing an error to
the upper layer is a good idea. All drivers are adapted accordingly.
There is no intended change of behaviour.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220708094922.1408248-4-u.kleine-koenig@pengutronix.de
2022-07-09 10:50:03 +02:00

516 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019-2020 Icenowy Zheng <icenowy@aosc.io>
*/
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#define K101_IM2BA02_INIT_CMD_LEN 2
static const char * const regulator_names[] = {
"dvdd",
"avdd",
"cvdd"
};
struct k101_im2ba02 {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
struct gpio_desc *reset;
};
static inline struct k101_im2ba02 *panel_to_k101_im2ba02(struct drm_panel *panel)
{
return container_of(panel, struct k101_im2ba02, panel);
}
struct k101_im2ba02_init_cmd {
u8 data[K101_IM2BA02_INIT_CMD_LEN];
};
static const struct k101_im2ba02_init_cmd k101_im2ba02_init_cmds[] = {
/* Switch to page 0 */
{ .data = { 0xE0, 0x00 } },
/* Seems to be some password */
{ .data = { 0xE1, 0x93} },
{ .data = { 0xE2, 0x65 } },
{ .data = { 0xE3, 0xF8 } },
/* Lane number, 0x02 - 3 lanes, 0x03 - 4 lanes */
{ .data = { 0x80, 0x03 } },
/* Sequence control */
{ .data = { 0x70, 0x02 } },
{ .data = { 0x71, 0x23 } },
{ .data = { 0x72, 0x06 } },
/* Switch to page 1 */
{ .data = { 0xE0, 0x01 } },
/* Set VCOM */
{ .data = { 0x00, 0x00 } },
{ .data = { 0x01, 0x66 } },
/* Set VCOM_Reverse */
{ .data = { 0x03, 0x00 } },
{ .data = { 0x04, 0x25 } },
/* Set Gamma Power, VG[MS][PN] */
{ .data = { 0x17, 0x00 } },
{ .data = { 0x18, 0x6D } },
{ .data = { 0x19, 0x00 } },
{ .data = { 0x1A, 0x00 } },
{ .data = { 0x1B, 0xBF } }, /* VGMN = -4.5V */
{ .data = { 0x1C, 0x00 } },
/* Set Gate Power */
{ .data = { 0x1F, 0x3E } }, /* VGH_R = 15V */
{ .data = { 0x20, 0x28 } }, /* VGL_R = -11V */
{ .data = { 0x21, 0x28 } }, /* VGL_R2 = -11V */
{ .data = { 0x22, 0x0E } }, /* PA[6:4] = 0, PA[0] = 0 */
/* Set Panel */
{ .data = { 0x37, 0x09 } }, /* SS = 1, BGR = 1 */
/* Set RGBCYC */
{ .data = { 0x38, 0x04 } }, /* JDT = 100 column inversion */
{ .data = { 0x39, 0x08 } }, /* RGB_N_EQ1 */
{ .data = { 0x3A, 0x12 } }, /* RGB_N_EQ2 */
{ .data = { 0x3C, 0x78 } }, /* set EQ3 for TE_H */
{ .data = { 0x3D, 0xFF } }, /* set CHGEN_ON */
{ .data = { 0x3E, 0xFF } }, /* set CHGEN_OFF */
{ .data = { 0x3F, 0x7F } }, /* set CHGEN_OFF2 */
/* Set TCON parameter */
{ .data = { 0x40, 0x06 } }, /* RSO = 800 points */
{ .data = { 0x41, 0xA0 } }, /* LN = 1280 lines */
/* Set power voltage */
{ .data = { 0x55, 0x0F } }, /* DCDCM */
{ .data = { 0x56, 0x01 } },
{ .data = { 0x57, 0x69 } },
{ .data = { 0x58, 0x0A } },
{ .data = { 0x59, 0x0A } },
{ .data = { 0x5A, 0x45 } },
{ .data = { 0x5B, 0x15 } },
/* Set gamma */
{ .data = { 0x5D, 0x7C } },
{ .data = { 0x5E, 0x65 } },
{ .data = { 0x5F, 0x55 } },
{ .data = { 0x60, 0x49 } },
{ .data = { 0x61, 0x44 } },
{ .data = { 0x62, 0x35 } },
{ .data = { 0x63, 0x3A } },
{ .data = { 0x64, 0x23 } },
{ .data = { 0x65, 0x3D } },
{ .data = { 0x66, 0x3C } },
{ .data = { 0x67, 0x3D } },
{ .data = { 0x68, 0x5D } },
{ .data = { 0x69, 0x4D } },
{ .data = { 0x6A, 0x56 } },
{ .data = { 0x6B, 0x48 } },
{ .data = { 0x6C, 0x45 } },
{ .data = { 0x6D, 0x38 } },
{ .data = { 0x6E, 0x25 } },
{ .data = { 0x6F, 0x00 } },
{ .data = { 0x70, 0x7C } },
{ .data = { 0x71, 0x65 } },
{ .data = { 0x72, 0x55 } },
{ .data = { 0x73, 0x49 } },
{ .data = { 0x74, 0x44 } },
{ .data = { 0x75, 0x35 } },
{ .data = { 0x76, 0x3A } },
{ .data = { 0x77, 0x23 } },
{ .data = { 0x78, 0x3D } },
{ .data = { 0x79, 0x3C } },
{ .data = { 0x7A, 0x3D } },
{ .data = { 0x7B, 0x5D } },
{ .data = { 0x7C, 0x4D } },
{ .data = { 0x7D, 0x56 } },
{ .data = { 0x7E, 0x48 } },
{ .data = { 0x7F, 0x45 } },
{ .data = { 0x80, 0x38 } },
{ .data = { 0x81, 0x25 } },
{ .data = { 0x82, 0x00 } },
/* Switch to page 2, for GIP */
{ .data = { 0xE0, 0x02 } },
{ .data = { 0x00, 0x1E } },
{ .data = { 0x01, 0x1E } },
{ .data = { 0x02, 0x41 } },
{ .data = { 0x03, 0x41 } },
{ .data = { 0x04, 0x43 } },
{ .data = { 0x05, 0x43 } },
{ .data = { 0x06, 0x1F } },
{ .data = { 0x07, 0x1F } },
{ .data = { 0x08, 0x1F } },
{ .data = { 0x09, 0x1F } },
{ .data = { 0x0A, 0x1E } },
{ .data = { 0x0B, 0x1E } },
{ .data = { 0x0C, 0x1F } },
{ .data = { 0x0D, 0x47 } },
{ .data = { 0x0E, 0x47 } },
{ .data = { 0x0F, 0x45 } },
{ .data = { 0x10, 0x45 } },
{ .data = { 0x11, 0x4B } },
{ .data = { 0x12, 0x4B } },
{ .data = { 0x13, 0x49 } },
{ .data = { 0x14, 0x49 } },
{ .data = { 0x15, 0x1F } },
{ .data = { 0x16, 0x1E } },
{ .data = { 0x17, 0x1E } },
{ .data = { 0x18, 0x40 } },
{ .data = { 0x19, 0x40 } },
{ .data = { 0x1A, 0x42 } },
{ .data = { 0x1B, 0x42 } },
{ .data = { 0x1C, 0x1F } },
{ .data = { 0x1D, 0x1F } },
{ .data = { 0x1E, 0x1F } },
{ .data = { 0x1F, 0x1f } },
{ .data = { 0x20, 0x1E } },
{ .data = { 0x21, 0x1E } },
{ .data = { 0x22, 0x1f } },
{ .data = { 0x23, 0x46 } },
{ .data = { 0x24, 0x46 } },
{ .data = { 0x25, 0x44 } },
{ .data = { 0x26, 0x44 } },
{ .data = { 0x27, 0x4A } },
{ .data = { 0x28, 0x4A } },
{ .data = { 0x29, 0x48 } },
{ .data = { 0x2A, 0x48 } },
{ .data = { 0x2B, 0x1f } },
{ .data = { 0x2C, 0x1F } },
{ .data = { 0x2D, 0x1F } },
{ .data = { 0x2E, 0x42 } },
{ .data = { 0x2F, 0x42 } },
{ .data = { 0x30, 0x40 } },
{ .data = { 0x31, 0x40 } },
{ .data = { 0x32, 0x1E } },
{ .data = { 0x33, 0x1E } },
{ .data = { 0x34, 0x1F } },
{ .data = { 0x35, 0x1F } },
{ .data = { 0x36, 0x1E } },
{ .data = { 0x37, 0x1E } },
{ .data = { 0x38, 0x1F } },
{ .data = { 0x39, 0x48 } },
{ .data = { 0x3A, 0x48 } },
{ .data = { 0x3B, 0x4A } },
{ .data = { 0x3C, 0x4A } },
{ .data = { 0x3D, 0x44 } },
{ .data = { 0x3E, 0x44 } },
{ .data = { 0x3F, 0x46 } },
{ .data = { 0x40, 0x46 } },
{ .data = { 0x41, 0x1F } },
{ .data = { 0x42, 0x1F } },
{ .data = { 0x43, 0x1F } },
{ .data = { 0x44, 0x43 } },
{ .data = { 0x45, 0x43 } },
{ .data = { 0x46, 0x41 } },
{ .data = { 0x47, 0x41 } },
{ .data = { 0x48, 0x1E } },
{ .data = { 0x49, 0x1E } },
{ .data = { 0x4A, 0x1E } },
{ .data = { 0x4B, 0x1F } },
{ .data = { 0x4C, 0x1E } },
{ .data = { 0x4D, 0x1E } },
{ .data = { 0x4E, 0x1F } },
{ .data = { 0x4F, 0x49 } },
{ .data = { 0x50, 0x49 } },
{ .data = { 0x51, 0x4B } },
{ .data = { 0x52, 0x4B } },
{ .data = { 0x53, 0x45 } },
{ .data = { 0x54, 0x45 } },
{ .data = { 0x55, 0x47 } },
{ .data = { 0x56, 0x47 } },
{ .data = { 0x57, 0x1F } },
{ .data = { 0x58, 0x10 } },
{ .data = { 0x59, 0x00 } },
{ .data = { 0x5A, 0x00 } },
{ .data = { 0x5B, 0x30 } },
{ .data = { 0x5C, 0x02 } },
{ .data = { 0x5D, 0x40 } },
{ .data = { 0x5E, 0x01 } },
{ .data = { 0x5F, 0x02 } },
{ .data = { 0x60, 0x30 } },
{ .data = { 0x61, 0x01 } },
{ .data = { 0x62, 0x02 } },
{ .data = { 0x63, 0x6A } },
{ .data = { 0x64, 0x6A } },
{ .data = { 0x65, 0x05 } },
{ .data = { 0x66, 0x12 } },
{ .data = { 0x67, 0x74 } },
{ .data = { 0x68, 0x04 } },
{ .data = { 0x69, 0x6A } },
{ .data = { 0x6A, 0x6A } },
{ .data = { 0x6B, 0x08 } },
{ .data = { 0x6C, 0x00 } },
{ .data = { 0x6D, 0x04 } },
{ .data = { 0x6E, 0x04 } },
{ .data = { 0x6F, 0x88 } },
{ .data = { 0x70, 0x00 } },
{ .data = { 0x71, 0x00 } },
{ .data = { 0x72, 0x06 } },
{ .data = { 0x73, 0x7B } },
{ .data = { 0x74, 0x00 } },
{ .data = { 0x75, 0x07 } },
{ .data = { 0x76, 0x00 } },
{ .data = { 0x77, 0x5D } },
{ .data = { 0x78, 0x17 } },
{ .data = { 0x79, 0x1F } },
{ .data = { 0x7A, 0x00 } },
{ .data = { 0x7B, 0x00 } },
{ .data = { 0x7C, 0x00 } },
{ .data = { 0x7D, 0x03 } },
{ .data = { 0x7E, 0x7B } },
{ .data = { 0xE0, 0x04 } },
{ .data = { 0x2B, 0x2B } },
{ .data = { 0x2E, 0x44 } },
{ .data = { 0xE0, 0x01 } },
{ .data = { 0x0E, 0x01 } },
{ .data = { 0xE0, 0x03 } },
{ .data = { 0x98, 0x2F } },
{ .data = { 0xE0, 0x00 } },
{ .data = { 0xE6, 0x02 } },
{ .data = { 0xE7, 0x02 } },
{ .data = { 0x11, 0x00 } },
};
static const struct k101_im2ba02_init_cmd timed_cmds[] = {
{ .data = { 0x29, 0x00 } },
{ .data = { 0x35, 0x00 } },
};
static int k101_im2ba02_prepare(struct drm_panel *panel)
{
struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
struct mipi_dsi_device *dsi = ctx->dsi;
unsigned int i;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret)
return ret;
msleep(30);
gpiod_set_value(ctx->reset, 1);
msleep(50);
gpiod_set_value(ctx->reset, 0);
msleep(50);
gpiod_set_value(ctx->reset, 1);
msleep(200);
for (i = 0; i < ARRAY_SIZE(k101_im2ba02_init_cmds); i++) {
const struct k101_im2ba02_init_cmd *cmd = &k101_im2ba02_init_cmds[i];
ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
if (ret < 0)
goto powerdown;
}
return 0;
powerdown:
gpiod_set_value(ctx->reset, 0);
msleep(50);
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
}
static int k101_im2ba02_enable(struct drm_panel *panel)
{
struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
const struct k101_im2ba02_init_cmd *cmd = &timed_cmds[1];
int ret;
msleep(150);
ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
if (ret < 0)
return ret;
msleep(50);
return mipi_dsi_dcs_write_buffer(ctx->dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
}
static int k101_im2ba02_disable(struct drm_panel *panel)
{
struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
return mipi_dsi_dcs_set_display_off(ctx->dsi);
}
static int k101_im2ba02_unprepare(struct drm_panel *panel)
{
struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
int ret;
ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
if (ret < 0)
dev_err(panel->dev, "failed to set display off: %d\n", ret);
ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
if (ret < 0)
dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);
msleep(200);
gpiod_set_value(ctx->reset, 0);
msleep(20);
return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
}
static const struct drm_display_mode k101_im2ba02_default_mode = {
.clock = 70000,
.hdisplay = 800,
.hsync_start = 800 + 20,
.hsync_end = 800 + 20 + 20,
.htotal = 800 + 20 + 20 + 20,
.vdisplay = 1280,
.vsync_start = 1280 + 16,
.vsync_end = 1280 + 16 + 4,
.vtotal = 1280 + 16 + 4 + 4,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
.width_mm = 136,
.height_mm = 217,
};
static int k101_im2ba02_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &k101_im2ba02_default_mode);
if (!mode) {
dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
k101_im2ba02_default_mode.hdisplay,
k101_im2ba02_default_mode.vdisplay,
drm_mode_vrefresh(&k101_im2ba02_default_mode));
return -ENOMEM;
}
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
drm_mode_probed_add(connector, mode);
return 1;
}
static const struct drm_panel_funcs k101_im2ba02_funcs = {
.disable = k101_im2ba02_disable,
.unprepare = k101_im2ba02_unprepare,
.prepare = k101_im2ba02_prepare,
.enable = k101_im2ba02_enable,
.get_modes = k101_im2ba02_get_modes,
};
static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi)
{
struct k101_im2ba02 *ctx;
unsigned int i;
int ret;
ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
ctx->supplies[i].supply = regulator_names[i];
ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies),
ctx->supplies);
if (ret < 0)
return dev_err_probe(&dsi->dev, ret, "Couldn't get regulators\n");
ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
"Couldn't get our reset GPIO\n");
drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs,
DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return ret;
drm_panel_add(&ctx->panel);
dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 4;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
static void k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi)
{
struct k101_im2ba02 *ctx = mipi_dsi_get_drvdata(dsi);
mipi_dsi_detach(dsi);
drm_panel_remove(&ctx->panel);
}
static const struct of_device_id k101_im2ba02_of_match[] = {
{ .compatible = "feixin,k101-im2ba02", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, k101_im2ba02_of_match);
static struct mipi_dsi_driver k101_im2ba02_driver = {
.probe = k101_im2ba02_dsi_probe,
.remove = k101_im2ba02_dsi_remove,
.driver = {
.name = "feixin-k101-im2ba02",
.of_match_table = k101_im2ba02_of_match,
},
};
module_mipi_dsi_driver(k101_im2ba02_driver);
MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
MODULE_DESCRIPTION("Feixin K101 IM2BA02 MIPI-DSI LCD panel");
MODULE_LICENSE("GPL");