brintos

brintos / linux-shallow public Read only

0
0
Text · 20.6 KiB · 28bfc48 Raw
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