brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · 22a1400 Raw
340 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver4 * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH5 *6 * based on7 *8 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver9 * Copyright (C) Purism SPC 201910 */11 12#include <drm/drm_mipi_dsi.h>13#include <drm/drm_modes.h>14#include <drm/drm_panel.h>15 16#include <video/display_timing.h>17#include <video/mipi_display.h>18 19#include <linux/delay.h>20#include <linux/gpio/consumer.h>21#include <linux/media-bus-format.h>22#include <linux/module.h>23#include <linux/of.h>24#include <linux/regulator/consumer.h>25 26/* Manufacturer specific Commands send via DSI */27#define XPP055C272_CMD_ALL_PIXEL_OFF	0x2228#define XPP055C272_CMD_ALL_PIXEL_ON	0x2329#define XPP055C272_CMD_SETDISP		0xb230#define XPP055C272_CMD_SETRGBIF		0xb331#define XPP055C272_CMD_SETCYC		0xb432#define XPP055C272_CMD_SETBGP		0xb533#define XPP055C272_CMD_SETVCOM		0xb634#define XPP055C272_CMD_SETOTP		0xb735#define XPP055C272_CMD_SETPOWER_EXT	0xb836#define XPP055C272_CMD_SETEXTC		0xb937#define XPP055C272_CMD_SETMIPI		0xbA38#define XPP055C272_CMD_SETVDC		0xbc39#define XPP055C272_CMD_SETPCR		0xbf40#define XPP055C272_CMD_SETSCR		0xc041#define XPP055C272_CMD_SETPOWER		0xc142#define XPP055C272_CMD_SETECO		0xc643#define XPP055C272_CMD_SETPANEL		0xcc44#define XPP055C272_CMD_SETGAMMA		0xe045#define XPP055C272_CMD_SETEQ		0xe346#define XPP055C272_CMD_SETGIP1		0xe947#define XPP055C272_CMD_SETGIP2		0xea48 49struct xpp055c272 {50	struct device *dev;51	struct drm_panel panel;52	struct gpio_desc *reset_gpio;53	struct regulator *vci;54	struct regulator *iovcc;55};56 57static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)58{59	return container_of(panel, struct xpp055c272, panel);60}61 62static int xpp055c272_init_sequence(struct xpp055c272 *ctx)63{64	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);65	struct device *dev = ctx->dev;66 67	/*68	 * Init sequence was supplied by the panel vendor without much69	 * documentation.70	 */71	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);72	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETMIPI,73			       0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,74			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,75			       0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,76			       0x00, 0x00, 0x37);77	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);78	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);79	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETRGBIF,80			       0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,81			       0x00, 0x00);82	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETSCR,83			       0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,84			       0x00);85	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);86	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);87	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);88	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);89	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEQ,90			       0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,91			       0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);92	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER,93			       0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,94			       0x67, 0x77, 0x33, 0x33);95	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,96			       0xff, 0x01, 0xff);97	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);98	msleep(20);99 100	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);101	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP1,102			       0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,103			       0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,104			       0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,105			       0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,106			       0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,107			       0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,108			       0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,109			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);110	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP2,111			       0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,112			       0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,113			       0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,114			       0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,115			       0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,116			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,117			       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,118			       0xa0, 0x00, 0x00, 0x00, 0x00);119	mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGAMMA,120			       0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,121			       0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,122			       0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,123			       0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,124			       0x11, 0x18);125 126	msleep(60);127 128	dev_dbg(dev, "Panel init sequence done\n");129	return 0;130}131 132static int xpp055c272_unprepare(struct drm_panel *panel)133{134	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);135	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);136	int ret;137 138	ret = mipi_dsi_dcs_set_display_off(dsi);139	if (ret < 0)140		dev_err(ctx->dev, "failed to set display off: %d\n", ret);141 142	mipi_dsi_dcs_enter_sleep_mode(dsi);143	if (ret < 0) {144		dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);145		return ret;146	}147 148	regulator_disable(ctx->iovcc);149	regulator_disable(ctx->vci);150 151	return 0;152}153 154static int xpp055c272_prepare(struct drm_panel *panel)155{156	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);157	struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);158	int ret;159 160	dev_dbg(ctx->dev, "Resetting the panel\n");161	ret = regulator_enable(ctx->vci);162	if (ret < 0) {163		dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);164		return ret;165	}166	ret = regulator_enable(ctx->iovcc);167	if (ret < 0) {168		dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);169		goto disable_vci;170	}171 172	gpiod_set_value_cansleep(ctx->reset_gpio, 1);173	/* T6: 10us */174	usleep_range(10, 20);175	gpiod_set_value_cansleep(ctx->reset_gpio, 0);176 177	/* T8: 20ms */178	msleep(20);179 180	ret = xpp055c272_init_sequence(ctx);181	if (ret < 0) {182		dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);183		goto disable_iovcc;184	}185 186	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);187	if (ret < 0) {188		dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);189		goto disable_iovcc;190	}191 192	/* T9: 120ms */193	msleep(120);194 195	ret = mipi_dsi_dcs_set_display_on(dsi);196	if (ret < 0) {197		dev_err(ctx->dev, "Failed to set display on: %d\n", ret);198		goto disable_iovcc;199	}200 201	msleep(50);202 203	return 0;204 205disable_iovcc:206	regulator_disable(ctx->iovcc);207disable_vci:208	regulator_disable(ctx->vci);209	return ret;210}211 212static const struct drm_display_mode default_mode = {213	.hdisplay	= 720,214	.hsync_start	= 720 + 40,215	.hsync_end	= 720 + 40 + 10,216	.htotal		= 720 + 40 + 10 + 40,217	.vdisplay	= 1280,218	.vsync_start	= 1280 + 22,219	.vsync_end	= 1280 + 22 + 4,220	.vtotal		= 1280 + 22 + 4 + 11,221	.clock		= 64000,222	.width_mm	= 68,223	.height_mm	= 121,224};225 226static int xpp055c272_get_modes(struct drm_panel *panel,227				struct drm_connector *connector)228{229	struct xpp055c272 *ctx = panel_to_xpp055c272(panel);230	struct drm_display_mode *mode;231 232	mode = drm_mode_duplicate(connector->dev, &default_mode);233	if (!mode) {234		dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",235			default_mode.hdisplay, default_mode.vdisplay,236			drm_mode_vrefresh(&default_mode));237		return -ENOMEM;238	}239 240	drm_mode_set_name(mode);241 242	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;243	connector->display_info.width_mm = mode->width_mm;244	connector->display_info.height_mm = mode->height_mm;245	drm_mode_probed_add(connector, mode);246 247	return 1;248}249 250static const struct drm_panel_funcs xpp055c272_funcs = {251	.unprepare	= xpp055c272_unprepare,252	.prepare	= xpp055c272_prepare,253	.get_modes	= xpp055c272_get_modes,254};255 256static int xpp055c272_probe(struct mipi_dsi_device *dsi)257{258	struct device *dev = &dsi->dev;259	struct xpp055c272 *ctx;260	int ret;261 262	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);263	if (!ctx)264		return -ENOMEM;265 266	ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);267	if (IS_ERR(ctx->reset_gpio))268		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),269				     "cannot get reset gpio\n");270 271	ctx->vci = devm_regulator_get(dev, "vci");272	if (IS_ERR(ctx->vci))273		return dev_err_probe(dev, PTR_ERR(ctx->vci),274				     "Failed to request vci regulator\n");275 276	ctx->iovcc = devm_regulator_get(dev, "iovcc");277	if (IS_ERR(ctx->iovcc))278		return dev_err_probe(dev, PTR_ERR(ctx->iovcc),279				     "Failed to request iovcc regulator\n");280 281	mipi_dsi_set_drvdata(dsi, ctx);282 283	ctx->dev = dev;284 285	dsi->lanes = 4;286	dsi->format = MIPI_DSI_FMT_RGB888;287	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |288			  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;289 290	drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,291		       DRM_MODE_CONNECTOR_DSI);292 293	ret = drm_panel_of_backlight(&ctx->panel);294	if (ret)295		return ret;296 297	drm_panel_add(&ctx->panel);298 299	ret = mipi_dsi_attach(dsi);300	if (ret < 0) {301		dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);302		drm_panel_remove(&ctx->panel);303		return ret;304	}305 306	return 0;307}308 309static void xpp055c272_remove(struct mipi_dsi_device *dsi)310{311	struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);312	int ret;313 314	ret = mipi_dsi_detach(dsi);315	if (ret < 0)316		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);317 318	drm_panel_remove(&ctx->panel);319}320 321static const struct of_device_id xpp055c272_of_match[] = {322	{ .compatible = "xinpeng,xpp055c272" },323	{ /* sentinel */ }324};325MODULE_DEVICE_TABLE(of, xpp055c272_of_match);326 327static struct mipi_dsi_driver xpp055c272_driver = {328	.driver = {329		.name = "panel-xinpeng-xpp055c272",330		.of_match_table = xpp055c272_of_match,331	},332	.probe	= xpp055c272_probe,333	.remove = xpp055c272_remove,334};335module_mipi_dsi_driver(xpp055c272_driver);336 337MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");338MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");339MODULE_LICENSE("GPL v2");340