405 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20174 *5 * Authors: Philippe Cornu <philippe.cornu@st.com>6 * Yannick Fertre <yannick.fertre@st.com>7 */8 9#include <linux/delay.h>10#include <linux/gpio/consumer.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/regulator/consumer.h>14 15#include <video/mipi_display.h>16 17#include <drm/drm_mipi_dsi.h>18#include <drm/drm_modes.h>19#include <drm/drm_panel.h>20 21/*** Manufacturer Command Set ***/22#define MCS_CMD_MODE_SW 0xFE /* CMD Mode Switch */23#define MCS_CMD1_UCS 0x00 /* User Command Set (UCS = CMD1) */24#define MCS_CMD2_P0 0x01 /* Manufacture Command Set Page0 (CMD2 P0) */25#define MCS_CMD2_P1 0x02 /* Manufacture Command Set Page1 (CMD2 P1) */26#define MCS_CMD2_P2 0x03 /* Manufacture Command Set Page2 (CMD2 P2) */27#define MCS_CMD2_P3 0x04 /* Manufacture Command Set Page3 (CMD2 P3) */28 29/* CMD2 P0 commands (Display Options and Power) */30#define MCS_STBCTR 0x12 /* TE1 Output Setting Zig-Zag Connection */31#define MCS_SGOPCTR 0x16 /* Source Bias Current */32#define MCS_SDCTR 0x1A /* Source Output Delay Time */33#define MCS_INVCTR 0x1B /* Inversion Type */34#define MCS_EXT_PWR_IC 0x24 /* External PWR IC Control */35#define MCS_SETAVDD 0x27 /* PFM Control for AVDD Output */36#define MCS_SETAVEE 0x29 /* PFM Control for AVEE Output */37#define MCS_BT2CTR 0x2B /* DDVDL Charge Pump Control */38#define MCS_BT3CTR 0x2F /* VGH Charge Pump Control */39#define MCS_BT4CTR 0x34 /* VGL Charge Pump Control */40#define MCS_VCMCTR 0x46 /* VCOM Output Level Control */41#define MCS_SETVGN 0x52 /* VG M/S N Control */42#define MCS_SETVGP 0x54 /* VG M/S P Control */43#define MCS_SW_CTRL 0x5F /* Interface Control for PFM and MIPI */44 45/* CMD2 P2 commands (GOA Timing Control) - no description in datasheet */46#define GOA_VSTV1 0x0047#define GOA_VSTV2 0x0748#define GOA_VCLK1 0x0E49#define GOA_VCLK2 0x1750#define GOA_VCLK_OPT1 0x2051#define GOA_BICLK1 0x2A52#define GOA_BICLK2 0x3753#define GOA_BICLK3 0x4454#define GOA_BICLK4 0x4F55#define GOA_BICLK_OPT1 0x5B56#define GOA_BICLK_OPT2 0x6057#define MCS_GOA_GPO1 0x6D58#define MCS_GOA_GPO2 0x7159#define MCS_GOA_EQ 0x7460#define MCS_GOA_CLK_GALLON 0x7C61#define MCS_GOA_FS_SEL0 0x7E62#define MCS_GOA_FS_SEL1 0x8763#define MCS_GOA_FS_SEL2 0x9164#define MCS_GOA_FS_SEL3 0x9B65#define MCS_GOA_BS_SEL0 0xAC66#define MCS_GOA_BS_SEL1 0xB567#define MCS_GOA_BS_SEL2 0xBF68#define MCS_GOA_BS_SEL3 0xC969#define MCS_GOA_BS_SEL4 0xD370 71/* CMD2 P3 commands (Gamma) */72#define MCS_GAMMA_VP 0x60 /* Gamma VP1~VP16 */73#define MCS_GAMMA_VN 0x70 /* Gamma VN1~VN16 */74 75struct rm68200 {76 struct device *dev;77 struct drm_panel panel;78 struct gpio_desc *reset_gpio;79 struct regulator *supply;80};81 82static const struct drm_display_mode default_mode = {83 .clock = 54000,84 .hdisplay = 720,85 .hsync_start = 720 + 48,86 .hsync_end = 720 + 48 + 9,87 .htotal = 720 + 48 + 9 + 48,88 .vdisplay = 1280,89 .vsync_start = 1280 + 12,90 .vsync_end = 1280 + 12 + 5,91 .vtotal = 1280 + 12 + 5 + 12,92 .flags = 0,93 .width_mm = 68,94 .height_mm = 122,95};96 97static inline struct rm68200 *panel_to_rm68200(struct drm_panel *panel)98{99 return container_of(panel, struct rm68200, panel);100}101 102static void rm68200_dcs_write_buf(struct rm68200 *ctx, const void *data,103 size_t len)104{105 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);106 int err;107 108 err = mipi_dsi_dcs_write_buffer(dsi, data, len);109 if (err < 0)110 dev_err_ratelimited(ctx->dev, "MIPI DSI DCS write buffer failed: %d\n", err);111}112 113static void rm68200_dcs_write_cmd(struct rm68200 *ctx, u8 cmd, u8 value)114{115 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);116 int err;117 118 err = mipi_dsi_dcs_write(dsi, cmd, &value, 1);119 if (err < 0)120 dev_err_ratelimited(ctx->dev, "MIPI DSI DCS write failed: %d\n", err);121}122 123#define dcs_write_seq(ctx, seq...) \124({ \125 static const u8 d[] = { seq }; \126 \127 rm68200_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \128})129 130/*131 * This panel is not able to auto-increment all cmd addresses so for some of132 * them, we need to send them one by one...133 */134#define dcs_write_cmd_seq(ctx, cmd, seq...) \135({ \136 static const u8 d[] = { seq }; \137 unsigned int i; \138 \139 for (i = 0; i < ARRAY_SIZE(d) ; i++) \140 rm68200_dcs_write_cmd(ctx, cmd + i, d[i]); \141})142 143static void rm68200_init_sequence(struct rm68200 *ctx)144{145 /* Enter CMD2 with page 0 */146 dcs_write_seq(ctx, MCS_CMD_MODE_SW, MCS_CMD2_P0);147 dcs_write_cmd_seq(ctx, MCS_EXT_PWR_IC, 0xC0, 0x53, 0x00);148 dcs_write_seq(ctx, MCS_BT2CTR, 0xE5);149 dcs_write_seq(ctx, MCS_SETAVDD, 0x0A);150 dcs_write_seq(ctx, MCS_SETAVEE, 0x0A);151 dcs_write_seq(ctx, MCS_SGOPCTR, 0x52);152 dcs_write_seq(ctx, MCS_BT3CTR, 0x53);153 dcs_write_seq(ctx, MCS_BT4CTR, 0x5A);154 dcs_write_seq(ctx, MCS_INVCTR, 0x00);155 dcs_write_seq(ctx, MCS_STBCTR, 0x0A);156 dcs_write_seq(ctx, MCS_SDCTR, 0x06);157 dcs_write_seq(ctx, MCS_VCMCTR, 0x56);158 dcs_write_seq(ctx, MCS_SETVGN, 0xA0, 0x00);159 dcs_write_seq(ctx, MCS_SETVGP, 0xA0, 0x00);160 dcs_write_seq(ctx, MCS_SW_CTRL, 0x11); /* 2 data lanes, see doc */161 162 dcs_write_seq(ctx, MCS_CMD_MODE_SW, MCS_CMD2_P2);163 dcs_write_seq(ctx, GOA_VSTV1, 0x05);164 dcs_write_seq(ctx, 0x02, 0x0B);165 dcs_write_seq(ctx, 0x03, 0x0F);166 dcs_write_seq(ctx, 0x04, 0x7D, 0x00, 0x50);167 dcs_write_cmd_seq(ctx, GOA_VSTV2, 0x05, 0x16, 0x0D, 0x11, 0x7D, 0x00,168 0x50);169 dcs_write_cmd_seq(ctx, GOA_VCLK1, 0x07, 0x08, 0x01, 0x02, 0x00, 0x7D,170 0x00, 0x85, 0x08);171 dcs_write_cmd_seq(ctx, GOA_VCLK2, 0x03, 0x04, 0x05, 0x06, 0x00, 0x7D,172 0x00, 0x85, 0x08);173 dcs_write_seq(ctx, GOA_VCLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,174 0x00, 0x00, 0x00, 0x00);175 dcs_write_cmd_seq(ctx, GOA_BICLK1, 0x07, 0x08);176 dcs_write_seq(ctx, 0x2D, 0x01);177 dcs_write_seq(ctx, 0x2F, 0x02, 0x00, 0x40, 0x05, 0x08, 0x54, 0x7D,178 0x00);179 dcs_write_cmd_seq(ctx, GOA_BICLK2, 0x03, 0x04, 0x05, 0x06, 0x00);180 dcs_write_seq(ctx, 0x3D, 0x40);181 dcs_write_seq(ctx, 0x3F, 0x05, 0x08, 0x54, 0x7D, 0x00);182 dcs_write_seq(ctx, GOA_BICLK3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,183 0x00, 0x00, 0x00, 0x00, 0x00);184 dcs_write_seq(ctx, GOA_BICLK4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,185 0x00, 0x00);186 dcs_write_seq(ctx, 0x58, 0x00, 0x00, 0x00);187 dcs_write_seq(ctx, GOA_BICLK_OPT1, 0x00, 0x00, 0x00, 0x00, 0x00);188 dcs_write_seq(ctx, GOA_BICLK_OPT2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);190 dcs_write_seq(ctx, MCS_GOA_GPO1, 0x00, 0x00, 0x00, 0x00);191 dcs_write_seq(ctx, MCS_GOA_GPO2, 0x00, 0x20, 0x00);192 dcs_write_seq(ctx, MCS_GOA_EQ, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,193 0x00, 0x00);194 dcs_write_seq(ctx, MCS_GOA_CLK_GALLON, 0x00, 0x00);195 dcs_write_cmd_seq(ctx, MCS_GOA_FS_SEL0, 0xBF, 0x02, 0x06, 0x14, 0x10,196 0x16, 0x12, 0x08, 0x3F);197 dcs_write_cmd_seq(ctx, MCS_GOA_FS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0C,198 0x0A, 0x0E, 0x3F, 0x3F, 0x00);199 dcs_write_cmd_seq(ctx, MCS_GOA_FS_SEL2, 0x04, 0x3F, 0x3F, 0x3F, 0x3F,200 0x05, 0x01, 0x3F, 0x3F, 0x0F);201 dcs_write_cmd_seq(ctx, MCS_GOA_FS_SEL3, 0x0B, 0x0D, 0x3F, 0x3F, 0x3F,202 0x3F);203 dcs_write_cmd_seq(ctx, 0xA2, 0x3F, 0x09, 0x13, 0x17, 0x11, 0x15);204 dcs_write_cmd_seq(ctx, 0xA9, 0x07, 0x03, 0x3F);205 dcs_write_cmd_seq(ctx, MCS_GOA_BS_SEL0, 0x3F, 0x05, 0x01, 0x17, 0x13,206 0x15, 0x11, 0x0F, 0x3F);207 dcs_write_cmd_seq(ctx, MCS_GOA_BS_SEL1, 0x3F, 0x3F, 0x3F, 0x3F, 0x0B,208 0x0D, 0x09, 0x3F, 0x3F, 0x07);209 dcs_write_cmd_seq(ctx, MCS_GOA_BS_SEL2, 0x03, 0x3F, 0x3F, 0x3F, 0x3F,210 0x02, 0x06, 0x3F, 0x3F, 0x08);211 dcs_write_cmd_seq(ctx, MCS_GOA_BS_SEL3, 0x0C, 0x0A, 0x3F, 0x3F, 0x3F,212 0x3F, 0x3F, 0x0E, 0x10, 0x14);213 dcs_write_cmd_seq(ctx, MCS_GOA_BS_SEL4, 0x12, 0x16, 0x00, 0x04, 0x3F);214 dcs_write_seq(ctx, 0xDC, 0x02);215 dcs_write_seq(ctx, 0xDE, 0x12);216 217 dcs_write_seq(ctx, MCS_CMD_MODE_SW, 0x0E); /* No documentation */218 dcs_write_seq(ctx, 0x01, 0x75);219 220 dcs_write_seq(ctx, MCS_CMD_MODE_SW, MCS_CMD2_P3);221 dcs_write_cmd_seq(ctx, MCS_GAMMA_VP, 0x00, 0x0C, 0x12, 0x0E, 0x06,222 0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,223 0x12, 0x0C, 0x00);224 dcs_write_cmd_seq(ctx, MCS_GAMMA_VN, 0x00, 0x0C, 0x12, 0x0E, 0x06,225 0x12, 0x0E, 0x0B, 0x15, 0x0B, 0x10, 0x07, 0x0F,226 0x12, 0x0C, 0x00);227 228 /* Exit CMD2 */229 dcs_write_seq(ctx, MCS_CMD_MODE_SW, MCS_CMD1_UCS);230}231 232static int rm68200_unprepare(struct drm_panel *panel)233{234 struct rm68200 *ctx = panel_to_rm68200(panel);235 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);236 int ret;237 238 ret = mipi_dsi_dcs_set_display_off(dsi);239 if (ret)240 dev_warn(panel->dev, "failed to set display off: %d\n", ret);241 242 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);243 if (ret)244 dev_warn(panel->dev, "failed to enter sleep mode: %d\n", ret);245 246 msleep(120);247 248 if (ctx->reset_gpio) {249 gpiod_set_value_cansleep(ctx->reset_gpio, 1);250 msleep(20);251 }252 253 regulator_disable(ctx->supply);254 255 return 0;256}257 258static int rm68200_prepare(struct drm_panel *panel)259{260 struct rm68200 *ctx = panel_to_rm68200(panel);261 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);262 int ret;263 264 ret = regulator_enable(ctx->supply);265 if (ret < 0) {266 dev_err(ctx->dev, "failed to enable supply: %d\n", ret);267 return ret;268 }269 270 if (ctx->reset_gpio) {271 gpiod_set_value_cansleep(ctx->reset_gpio, 1);272 msleep(20);273 gpiod_set_value_cansleep(ctx->reset_gpio, 0);274 msleep(100);275 }276 277 rm68200_init_sequence(ctx);278 279 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);280 if (ret)281 return ret;282 283 msleep(125);284 285 ret = mipi_dsi_dcs_set_display_on(dsi);286 if (ret)287 return ret;288 289 msleep(20);290 291 return 0;292}293 294static int rm68200_get_modes(struct drm_panel *panel,295 struct drm_connector *connector)296{297 struct drm_display_mode *mode;298 299 mode = drm_mode_duplicate(connector->dev, &default_mode);300 if (!mode) {301 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",302 default_mode.hdisplay, default_mode.vdisplay,303 drm_mode_vrefresh(&default_mode));304 return -ENOMEM;305 }306 307 drm_mode_set_name(mode);308 309 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;310 drm_mode_probed_add(connector, mode);311 312 connector->display_info.width_mm = mode->width_mm;313 connector->display_info.height_mm = mode->height_mm;314 315 return 1;316}317 318static const struct drm_panel_funcs rm68200_drm_funcs = {319 .unprepare = rm68200_unprepare,320 .prepare = rm68200_prepare,321 .get_modes = rm68200_get_modes,322};323 324static int rm68200_probe(struct mipi_dsi_device *dsi)325{326 struct device *dev = &dsi->dev;327 struct rm68200 *ctx;328 int ret;329 330 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);331 if (!ctx)332 return -ENOMEM;333 334 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);335 if (IS_ERR(ctx->reset_gpio)) {336 ret = PTR_ERR(ctx->reset_gpio);337 dev_err(dev, "cannot get reset GPIO: %d\n", ret);338 return ret;339 }340 341 ctx->supply = devm_regulator_get(dev, "power");342 if (IS_ERR(ctx->supply)) {343 ret = PTR_ERR(ctx->supply);344 if (ret != -EPROBE_DEFER)345 dev_err(dev, "cannot get regulator: %d\n", ret);346 return ret;347 }348 349 mipi_dsi_set_drvdata(dsi, ctx);350 351 ctx->dev = dev;352 353 dsi->lanes = 2;354 dsi->format = MIPI_DSI_FMT_RGB888;355 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |356 MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS;357 358 drm_panel_init(&ctx->panel, dev, &rm68200_drm_funcs,359 DRM_MODE_CONNECTOR_DSI);360 361 ret = drm_panel_of_backlight(&ctx->panel);362 if (ret)363 return ret;364 365 drm_panel_add(&ctx->panel);366 367 ret = mipi_dsi_attach(dsi);368 if (ret < 0) {369 dev_err(dev, "mipi_dsi_attach() failed: %d\n", ret);370 drm_panel_remove(&ctx->panel);371 return ret;372 }373 374 return 0;375}376 377static void rm68200_remove(struct mipi_dsi_device *dsi)378{379 struct rm68200 *ctx = mipi_dsi_get_drvdata(dsi);380 381 mipi_dsi_detach(dsi);382 drm_panel_remove(&ctx->panel);383}384 385static const struct of_device_id raydium_rm68200_of_match[] = {386 { .compatible = "raydium,rm68200" },387 { }388};389MODULE_DEVICE_TABLE(of, raydium_rm68200_of_match);390 391static struct mipi_dsi_driver raydium_rm68200_driver = {392 .probe = rm68200_probe,393 .remove = rm68200_remove,394 .driver = {395 .name = "panel-raydium-rm68200",396 .of_match_table = raydium_rm68200_of_match,397 },398};399module_mipi_dsi_driver(raydium_rm68200_driver);400 401MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");402MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");403MODULE_DESCRIPTION("DRM Driver for Raydium RM68200 MIPI DSI panel");404MODULE_LICENSE("GPL v2");405