695 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2017 Free Electrons4 */5 6#include <linux/delay.h>7#include <linux/gpio/consumer.h>8#include <linux/module.h>9#include <linux/regulator/consumer.h>10#include <linux/spi/spi.h>11 12#include <video/mipi_display.h>13#include <linux/media-bus-format.h>14 15#include <drm/drm_device.h>16#include <drm/drm_modes.h>17#include <drm/drm_panel.h>18 19#define ST7789V_RAMCTRL_CMD 0xb020#define ST7789V_RAMCTRL_RM_RGB BIT(4)21#define ST7789V_RAMCTRL_DM_RGB BIT(0)22#define ST7789V_RAMCTRL_MAGIC (3 << 6)23#define ST7789V_RAMCTRL_EPF(n) (((n) & 3) << 4)24 25#define ST7789V_RGBCTRL_CMD 0xb126#define ST7789V_RGBCTRL_WO BIT(7)27#define ST7789V_RGBCTRL_RCM(n) (((n) & 3) << 5)28#define ST7789V_RGBCTRL_VSYNC_HIGH BIT(3)29#define ST7789V_RGBCTRL_HSYNC_HIGH BIT(2)30#define ST7789V_RGBCTRL_PCLK_FALLING BIT(1)31#define ST7789V_RGBCTRL_DE_LOW BIT(0)32#define ST7789V_RGBCTRL_VBP(n) ((n) & 0x7f)33#define ST7789V_RGBCTRL_HBP(n) ((n) & 0x1f)34 35#define ST7789V_PORCTRL_CMD 0xb236#define ST7789V_PORCTRL_IDLE_BP(n) (((n) & 0xf) << 4)37#define ST7789V_PORCTRL_IDLE_FP(n) ((n) & 0xf)38#define ST7789V_PORCTRL_PARTIAL_BP(n) (((n) & 0xf) << 4)39#define ST7789V_PORCTRL_PARTIAL_FP(n) ((n) & 0xf)40 41#define ST7789V_GCTRL_CMD 0xb742#define ST7789V_GCTRL_VGHS(n) (((n) & 7) << 4)43#define ST7789V_GCTRL_VGLS(n) ((n) & 7)44 45#define ST7789V_VCOMS_CMD 0xbb46 47#define ST7789V_LCMCTRL_CMD 0xc048#define ST7789V_LCMCTRL_XBGR BIT(5)49#define ST7789V_LCMCTRL_XMX BIT(3)50#define ST7789V_LCMCTRL_XMH BIT(2)51 52#define ST7789V_VDVVRHEN_CMD 0xc253#define ST7789V_VDVVRHEN_CMDEN BIT(0)54 55#define ST7789V_VRHS_CMD 0xc356 57#define ST7789V_VDVS_CMD 0xc458 59#define ST7789V_FRCTRL2_CMD 0xc660 61#define ST7789V_PWCTRL1_CMD 0xd062#define ST7789V_PWCTRL1_MAGIC 0xa463#define ST7789V_PWCTRL1_AVDD(n) (((n) & 3) << 6)64#define ST7789V_PWCTRL1_AVCL(n) (((n) & 3) << 4)65#define ST7789V_PWCTRL1_VDS(n) ((n) & 3)66 67#define ST7789V_PVGAMCTRL_CMD 0xe068#define ST7789V_PVGAMCTRL_JP0(n) (((n) & 3) << 4)69#define ST7789V_PVGAMCTRL_JP1(n) (((n) & 3) << 4)70#define ST7789V_PVGAMCTRL_VP0(n) ((n) & 0xf)71#define ST7789V_PVGAMCTRL_VP1(n) ((n) & 0x3f)72#define ST7789V_PVGAMCTRL_VP2(n) ((n) & 0x3f)73#define ST7789V_PVGAMCTRL_VP4(n) ((n) & 0x1f)74#define ST7789V_PVGAMCTRL_VP6(n) ((n) & 0x1f)75#define ST7789V_PVGAMCTRL_VP13(n) ((n) & 0xf)76#define ST7789V_PVGAMCTRL_VP20(n) ((n) & 0x7f)77#define ST7789V_PVGAMCTRL_VP27(n) ((n) & 7)78#define ST7789V_PVGAMCTRL_VP36(n) (((n) & 7) << 4)79#define ST7789V_PVGAMCTRL_VP43(n) ((n) & 0x7f)80#define ST7789V_PVGAMCTRL_VP50(n) ((n) & 0xf)81#define ST7789V_PVGAMCTRL_VP57(n) ((n) & 0x1f)82#define ST7789V_PVGAMCTRL_VP59(n) ((n) & 0x1f)83#define ST7789V_PVGAMCTRL_VP61(n) ((n) & 0x3f)84#define ST7789V_PVGAMCTRL_VP62(n) ((n) & 0x3f)85#define ST7789V_PVGAMCTRL_VP63(n) (((n) & 0xf) << 4)86 87#define ST7789V_NVGAMCTRL_CMD 0xe188#define ST7789V_NVGAMCTRL_JN0(n) (((n) & 3) << 4)89#define ST7789V_NVGAMCTRL_JN1(n) (((n) & 3) << 4)90#define ST7789V_NVGAMCTRL_VN0(n) ((n) & 0xf)91#define ST7789V_NVGAMCTRL_VN1(n) ((n) & 0x3f)92#define ST7789V_NVGAMCTRL_VN2(n) ((n) & 0x3f)93#define ST7789V_NVGAMCTRL_VN4(n) ((n) & 0x1f)94#define ST7789V_NVGAMCTRL_VN6(n) ((n) & 0x1f)95#define ST7789V_NVGAMCTRL_VN13(n) ((n) & 0xf)96#define ST7789V_NVGAMCTRL_VN20(n) ((n) & 0x7f)97#define ST7789V_NVGAMCTRL_VN27(n) ((n) & 7)98#define ST7789V_NVGAMCTRL_VN36(n) (((n) & 7) << 4)99#define ST7789V_NVGAMCTRL_VN43(n) ((n) & 0x7f)100#define ST7789V_NVGAMCTRL_VN50(n) ((n) & 0xf)101#define ST7789V_NVGAMCTRL_VN57(n) ((n) & 0x1f)102#define ST7789V_NVGAMCTRL_VN59(n) ((n) & 0x1f)103#define ST7789V_NVGAMCTRL_VN61(n) ((n) & 0x3f)104#define ST7789V_NVGAMCTRL_VN62(n) ((n) & 0x3f)105#define ST7789V_NVGAMCTRL_VN63(n) (((n) & 0xf) << 4)106 107#define ST7789V_TEST(val, func) \108 do { \109 if ((val = (func))) \110 return val; \111 } while (0)112 113#define ST7789V_IDS { 0x85, 0x85, 0x52 }114#define ST7789V_IDS_SIZE 3115 116struct st7789_panel_info {117 const struct drm_display_mode *mode;118 u32 bus_format;119 u32 bus_flags;120 bool invert_mode;121 bool partial_mode;122 u16 partial_start;123 u16 partial_end;124};125 126struct st7789v {127 struct drm_panel panel;128 const struct st7789_panel_info *info;129 struct spi_device *spi;130 struct gpio_desc *reset;131 struct regulator *power;132 enum drm_panel_orientation orientation;133};134 135enum st7789v_prefix {136 ST7789V_COMMAND = 0,137 ST7789V_DATA = 1,138};139 140static inline struct st7789v *panel_to_st7789v(struct drm_panel *panel)141{142 return container_of(panel, struct st7789v, panel);143}144 145static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix,146 u8 data)147{148 struct spi_transfer xfer = { };149 u16 txbuf = ((prefix & 1) << 8) | data;150 151 xfer.tx_buf = &txbuf;152 xfer.len = sizeof(txbuf);153 154 return spi_sync_transfer(ctx->spi, &xfer, 1);155}156 157static int st7789v_write_command(struct st7789v *ctx, u8 cmd)158{159 return st7789v_spi_write(ctx, ST7789V_COMMAND, cmd);160}161 162static int st7789v_write_data(struct st7789v *ctx, u8 cmd)163{164 return st7789v_spi_write(ctx, ST7789V_DATA, cmd);165}166 167static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,168 unsigned int len)169{170 struct spi_transfer xfer[2] = { };171 struct spi_message msg;172 u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;173 u16 rxbuf[4] = {};174 u8 bit9 = 0;175 int ret, i;176 177 switch (len) {178 case 1:179 case 3:180 case 4:181 break;182 default:183 return -EOPNOTSUPP;184 }185 186 spi_message_init(&msg);187 188 xfer[0].tx_buf = &txbuf;189 xfer[0].len = sizeof(txbuf);190 spi_message_add_tail(&xfer[0], &msg);191 192 xfer[1].rx_buf = rxbuf;193 xfer[1].len = len * 2;194 spi_message_add_tail(&xfer[1], &msg);195 196 ret = spi_sync(ctx->spi, &msg);197 if (ret)198 return ret;199 200 for (i = 0; i < len; i++) {201 buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));202 if (i)203 bit9 = rxbuf[i] & GENMASK(i - 1, 0);204 }205 206 return 0;207}208 209static int st7789v_check_id(struct drm_panel *panel)210{211 const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;212 struct st7789v *ctx = panel_to_st7789v(panel);213 bool invalid_ids = false;214 int ret, i;215 u8 ids[3];216 217 if (ctx->spi->mode & SPI_NO_RX)218 return 0;219 220 ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);221 if (ret)222 return ret;223 224 for (i = 0; i < ST7789V_IDS_SIZE; i++) {225 if (ids[i] != st7789v_ids[i]) {226 invalid_ids = true;227 break;228 }229 }230 231 if (invalid_ids)232 return -EIO;233 234 return 0;235}236 237static const struct drm_display_mode default_mode = {238 .clock = 7000,239 .hdisplay = 240,240 .hsync_start = 240 + 38,241 .hsync_end = 240 + 38 + 10,242 .htotal = 240 + 38 + 10 + 10,243 .vdisplay = 320,244 .vsync_start = 320 + 8,245 .vsync_end = 320 + 8 + 4,246 .vtotal = 320 + 8 + 4 + 4,247 .width_mm = 61,248 .height_mm = 103,249 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,250};251 252static const struct drm_display_mode t28cp45tn89_mode = {253 .clock = 6008,254 .hdisplay = 240,255 .hsync_start = 240 + 38,256 .hsync_end = 240 + 38 + 10,257 .htotal = 240 + 38 + 10 + 10,258 .vdisplay = 320,259 .vsync_start = 320 + 8,260 .vsync_end = 320 + 8 + 4,261 .vtotal = 320 + 8 + 4 + 4,262 .width_mm = 43,263 .height_mm = 57,264 .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC,265};266 267static const struct drm_display_mode et028013dma_mode = {268 .clock = 3000,269 .hdisplay = 240,270 .hsync_start = 240 + 38,271 .hsync_end = 240 + 38 + 10,272 .htotal = 240 + 38 + 10 + 10,273 .vdisplay = 320,274 .vsync_start = 320 + 8,275 .vsync_end = 320 + 8 + 4,276 .vtotal = 320 + 8 + 4 + 4,277 .width_mm = 43,278 .height_mm = 58,279 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,280};281 282static const struct drm_display_mode jt240mhqs_hwt_ek_e3_mode = {283 .clock = 6000,284 .hdisplay = 240,285 .hsync_start = 240 + 38,286 .hsync_end = 240 + 38 + 10,287 .htotal = 240 + 38 + 10 + 10,288 .vdisplay = 280,289 .vsync_start = 280 + 48,290 .vsync_end = 280 + 48 + 4,291 .vtotal = 280 + 48 + 4 + 4,292 .width_mm = 37,293 .height_mm = 43,294 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,295};296 297static const struct st7789_panel_info default_panel = {298 .mode = &default_mode,299 .invert_mode = true,300 .bus_format = MEDIA_BUS_FMT_RGB666_1X18,301 .bus_flags = DRM_BUS_FLAG_DE_HIGH |302 DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,303};304 305static const struct st7789_panel_info t28cp45tn89_panel = {306 .mode = &t28cp45tn89_mode,307 .invert_mode = false,308 .bus_format = MEDIA_BUS_FMT_RGB565_1X16,309 .bus_flags = DRM_BUS_FLAG_DE_HIGH |310 DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,311};312 313static const struct st7789_panel_info et028013dma_panel = {314 .mode = &et028013dma_mode,315 .invert_mode = true,316 .bus_format = MEDIA_BUS_FMT_RGB666_1X18,317 .bus_flags = DRM_BUS_FLAG_DE_HIGH |318 DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,319};320 321static const struct st7789_panel_info jt240mhqs_hwt_ek_e3_panel = {322 .mode = &jt240mhqs_hwt_ek_e3_mode,323 .invert_mode = true,324 .bus_format = MEDIA_BUS_FMT_RGB666_1X18,325 .bus_flags = DRM_BUS_FLAG_DE_HIGH |326 DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,327 .partial_mode = true,328 .partial_start = 38,329 .partial_end = 318,330};331 332static int st7789v_get_modes(struct drm_panel *panel,333 struct drm_connector *connector)334{335 struct st7789v *ctx = panel_to_st7789v(panel);336 struct drm_display_mode *mode;337 338 mode = drm_mode_duplicate(connector->dev, ctx->info->mode);339 if (!mode) {340 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",341 ctx->info->mode->hdisplay, ctx->info->mode->vdisplay,342 drm_mode_vrefresh(ctx->info->mode));343 return -ENOMEM;344 }345 346 drm_mode_set_name(mode);347 348 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;349 drm_mode_probed_add(connector, mode);350 351 connector->display_info.bpc = 6;352 connector->display_info.width_mm = ctx->info->mode->width_mm;353 connector->display_info.height_mm = ctx->info->mode->height_mm;354 connector->display_info.bus_flags = ctx->info->bus_flags;355 drm_display_info_set_bus_formats(&connector->display_info,356 &ctx->info->bus_format, 1);357 358 /*359 * TODO: Remove once all drm drivers call360 * drm_connector_set_orientation_from_panel()361 */362 drm_connector_set_panel_orientation(connector, ctx->orientation);363 364 return 1;365}366 367static enum drm_panel_orientation st7789v_get_orientation(struct drm_panel *p)368{369 struct st7789v *ctx = panel_to_st7789v(p);370 371 return ctx->orientation;372}373 374static int st7789v_prepare(struct drm_panel *panel)375{376 struct st7789v *ctx = panel_to_st7789v(panel);377 u8 mode, pixel_fmt, polarity;378 int ret;379 380 if (!ctx->info->partial_mode)381 mode = ST7789V_RGBCTRL_WO;382 else383 mode = 0;384 385 switch (ctx->info->bus_format) {386 case MEDIA_BUS_FMT_RGB666_1X18:387 pixel_fmt = MIPI_DCS_PIXEL_FMT_18BIT;388 break;389 case MEDIA_BUS_FMT_RGB565_1X16:390 pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT;391 break;392 default:393 dev_err(panel->dev, "unsupported bus format: %d\n",394 ctx->info->bus_format);395 return -EINVAL;396 }397 398 pixel_fmt = (pixel_fmt << 4) | pixel_fmt;399 400 polarity = 0;401 if (ctx->info->mode->flags & DRM_MODE_FLAG_PVSYNC)402 polarity |= ST7789V_RGBCTRL_VSYNC_HIGH;403 if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC)404 polarity |= ST7789V_RGBCTRL_HSYNC_HIGH;405 if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE)406 polarity |= ST7789V_RGBCTRL_PCLK_FALLING;407 if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW)408 polarity |= ST7789V_RGBCTRL_DE_LOW;409 410 ret = regulator_enable(ctx->power);411 if (ret)412 return ret;413 414 gpiod_set_value(ctx->reset, 1);415 msleep(30);416 gpiod_set_value(ctx->reset, 0);417 msleep(120);418 419 /*420 * Avoid failing if the IDs are invalid in case the Rx bus width421 * description is missing.422 */423 ret = st7789v_check_id(panel);424 if (ret)425 dev_warn(panel->dev, "Unrecognized panel IDs");426 427 ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));428 429 /* We need to wait 120ms after a sleep out command */430 msleep(120);431 432 ST7789V_TEST(ret, st7789v_write_command(ctx,433 MIPI_DCS_SET_ADDRESS_MODE));434 ST7789V_TEST(ret, st7789v_write_data(ctx, 0));435 436 ST7789V_TEST(ret, st7789v_write_command(ctx,437 MIPI_DCS_SET_PIXEL_FORMAT));438 ST7789V_TEST(ret, st7789v_write_data(ctx, pixel_fmt));439 440 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD));441 ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));442 ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));443 ST7789V_TEST(ret, st7789v_write_data(ctx, 0));444 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PORCTRL_IDLE_BP(3) |445 ST7789V_PORCTRL_IDLE_FP(3)));446 ST7789V_TEST(ret, st7789v_write_data(ctx,447 ST7789V_PORCTRL_PARTIAL_BP(3) |448 ST7789V_PORCTRL_PARTIAL_FP(3)));449 450 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_GCTRL_CMD));451 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_GCTRL_VGLS(5) |452 ST7789V_GCTRL_VGHS(3)));453 454 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VCOMS_CMD));455 ST7789V_TEST(ret, st7789v_write_data(ctx, 0x2b));456 457 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_LCMCTRL_CMD));458 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_LCMCTRL_XMH |459 ST7789V_LCMCTRL_XMX |460 ST7789V_LCMCTRL_XBGR));461 462 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVVRHEN_CMD));463 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_VDVVRHEN_CMDEN));464 465 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VRHS_CMD));466 ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));467 468 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVS_CMD));469 ST7789V_TEST(ret, st7789v_write_data(ctx, 0x20));470 471 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_FRCTRL2_CMD));472 ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));473 474 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PWCTRL1_CMD));475 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_MAGIC));476 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_AVDD(2) |477 ST7789V_PWCTRL1_AVCL(2) |478 ST7789V_PWCTRL1_VDS(1)));479 480 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PVGAMCTRL_CMD));481 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP63(0xd)));482 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP1(0xca)));483 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP2(0xe)));484 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP4(8)));485 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP6(9)));486 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP13(7)));487 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP20(0x2d)));488 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP27(0xb) |489 ST7789V_PVGAMCTRL_VP36(3)));490 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP43(0x3d)));491 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_JP1(3) |492 ST7789V_PVGAMCTRL_VP50(4)));493 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP57(0xa)));494 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP59(0xa)));495 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP61(0x1b)));496 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP62(0x28)));497 498 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_NVGAMCTRL_CMD));499 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN63(0xd)));500 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN1(0xca)));501 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN2(0xf)));502 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN4(8)));503 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN6(8)));504 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN13(7)));505 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN20(0x2e)));506 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN27(0xc) |507 ST7789V_NVGAMCTRL_VN36(5)));508 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN43(0x40)));509 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_JN1(3) |510 ST7789V_NVGAMCTRL_VN50(4)));511 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN57(9)));512 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN59(0xb)));513 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN61(0x1b)));514 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN62(0x28)));515 516 if (ctx->info->invert_mode) {517 ST7789V_TEST(ret, st7789v_write_command(ctx,518 MIPI_DCS_ENTER_INVERT_MODE));519 } else {520 ST7789V_TEST(ret, st7789v_write_command(ctx,521 MIPI_DCS_EXIT_INVERT_MODE));522 }523 524 if (ctx->info->partial_mode) {525 u8 area_data[4] = {526 (ctx->info->partial_start >> 8) & 0xff,527 (ctx->info->partial_start >> 0) & 0xff,528 ((ctx->info->partial_end - 1) >> 8) & 0xff,529 ((ctx->info->partial_end - 1) >> 0) & 0xff,530 };531 532 /* Caution: if userspace ever pushes a mode different from the533 * expected one (i.e., the one advertised by get_modes), we'll534 * add margins.535 */536 537 ST7789V_TEST(ret, st7789v_write_command(538 ctx, MIPI_DCS_ENTER_PARTIAL_MODE));539 540 ST7789V_TEST(ret, st7789v_write_command(541 ctx, MIPI_DCS_SET_PAGE_ADDRESS));542 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[0]));543 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[1]));544 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[2]));545 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[3]));546 547 ST7789V_TEST(ret, st7789v_write_command(548 ctx, MIPI_DCS_SET_PARTIAL_ROWS));549 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[0]));550 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[1]));551 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[2]));552 ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[3]));553 }554 555 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD));556 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB |557 ST7789V_RAMCTRL_RM_RGB));558 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_EPF(3) |559 ST7789V_RAMCTRL_MAGIC));560 561 ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD));562 ST7789V_TEST(ret, st7789v_write_data(ctx, mode |563 ST7789V_RGBCTRL_RCM(2) |564 polarity));565 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));566 ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));567 568 return 0;569}570 571static int st7789v_enable(struct drm_panel *panel)572{573 struct st7789v *ctx = panel_to_st7789v(panel);574 575 return st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_ON);576}577 578static int st7789v_disable(struct drm_panel *panel)579{580 struct st7789v *ctx = panel_to_st7789v(panel);581 int ret;582 583 ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_OFF));584 585 return 0;586}587 588static int st7789v_unprepare(struct drm_panel *panel)589{590 struct st7789v *ctx = panel_to_st7789v(panel);591 int ret;592 593 ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_SLEEP_MODE));594 595 regulator_disable(ctx->power);596 597 return 0;598}599 600static const struct drm_panel_funcs st7789v_drm_funcs = {601 .disable = st7789v_disable,602 .enable = st7789v_enable,603 .get_modes = st7789v_get_modes,604 .get_orientation = st7789v_get_orientation,605 .prepare = st7789v_prepare,606 .unprepare = st7789v_unprepare,607};608 609static int st7789v_probe(struct spi_device *spi)610{611 struct device *dev = &spi->dev;612 struct st7789v *ctx;613 int ret;614 615 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);616 if (!ctx)617 return -ENOMEM;618 619 spi_set_drvdata(spi, ctx);620 ctx->spi = spi;621 622 spi->bits_per_word = 9;623 ret = spi_setup(spi);624 if (ret < 0)625 return dev_err_probe(&spi->dev, ret, "Failed to setup spi\n");626 627 ctx->info = device_get_match_data(&spi->dev);628 629 drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs,630 DRM_MODE_CONNECTOR_DPI);631 632 ctx->power = devm_regulator_get(dev, "power");633 ret = PTR_ERR_OR_ZERO(ctx->power);634 if (ret)635 return dev_err_probe(dev, ret, "Failed to get regulator\n");636 637 ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);638 ret = PTR_ERR_OR_ZERO(ctx->reset);639 if (ret)640 return dev_err_probe(dev, ret, "Failed to get reset line\n");641 642 ret = drm_panel_of_backlight(&ctx->panel);643 if (ret)644 return dev_err_probe(dev, ret, "Failed to get backlight\n");645 646 ret = of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation);647 if (ret)648 return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n");649 650 drm_panel_add(&ctx->panel);651 652 return 0;653}654 655static void st7789v_remove(struct spi_device *spi)656{657 struct st7789v *ctx = spi_get_drvdata(spi);658 659 drm_panel_remove(&ctx->panel);660}661 662static const struct spi_device_id st7789v_spi_id[] = {663 { "st7789v", (unsigned long) &default_panel },664 { "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel },665 { "et028013dma", (unsigned long) &et028013dma_panel },666 { "jt240mhqs-hwt-ek-e3", (unsigned long) &jt240mhqs_hwt_ek_e3_panel },667 { }668};669MODULE_DEVICE_TABLE(spi, st7789v_spi_id);670 671static const struct of_device_id st7789v_of_match[] = {672 { .compatible = "sitronix,st7789v", .data = &default_panel },673 { .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel },674 { .compatible = "edt,et028013dma", .data = &et028013dma_panel },675 { .compatible = "jasonic,jt240mhqs-hwt-ek-e3",676 .data = &jt240mhqs_hwt_ek_e3_panel },677 { }678};679MODULE_DEVICE_TABLE(of, st7789v_of_match);680 681static struct spi_driver st7789v_driver = {682 .probe = st7789v_probe,683 .remove = st7789v_remove,684 .id_table = st7789v_spi_id,685 .driver = {686 .name = "st7789v",687 .of_match_table = st7789v_of_match,688 },689};690module_spi_driver(st7789v_driver);691 692MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");693MODULE_DESCRIPTION("Sitronix st7789v LCD Driver");694MODULE_LICENSE("GPL v2");695