Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Heiko Stübner | 2045 | 100.00% | 1 | 100.00% |
Total | 2045 | 1 |
// SPDX-License-Identifier: GPL-2.0 /* * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH * * based on * * Rockteck jh057n00900 5.5" MIPI-DSI panel driver * Copyright (C) Purism SPC 2019 */ #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> #include <video/display_timing.h> #include <video/mipi_display.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regulator/consumer.h> /* Manufacturer specific Commands send via DSI */ #define XPP055C272_CMD_ALL_PIXEL_OFF 0x22 #define XPP055C272_CMD_ALL_PIXEL_ON 0x23 #define XPP055C272_CMD_SETDISP 0xb2 #define XPP055C272_CMD_SETRGBIF 0xb3 #define XPP055C272_CMD_SETCYC 0xb4 #define XPP055C272_CMD_SETBGP 0xb5 #define XPP055C272_CMD_SETVCOM 0xb6 #define XPP055C272_CMD_SETOTP 0xb7 #define XPP055C272_CMD_SETPOWER_EXT 0xb8 #define XPP055C272_CMD_SETEXTC 0xb9 #define XPP055C272_CMD_SETMIPI 0xbA #define XPP055C272_CMD_SETVDC 0xbc #define XPP055C272_CMD_SETPCR 0xbf #define XPP055C272_CMD_SETSCR 0xc0 #define XPP055C272_CMD_SETPOWER 0xc1 #define XPP055C272_CMD_SETECO 0xc6 #define XPP055C272_CMD_SETPANEL 0xcc #define XPP055C272_CMD_SETGAMMA 0xe0 #define XPP055C272_CMD_SETEQ 0xe3 #define XPP055C272_CMD_SETGIP1 0xe9 #define XPP055C272_CMD_SETGIP2 0xea struct xpp055c272 { struct device *dev; struct drm_panel panel; struct gpio_desc *reset_gpio; struct regulator *vci; struct regulator *iovcc; bool prepared; }; static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) { return container_of(panel, struct xpp055c272, panel); } #define dsi_generic_write_seq(dsi, cmd, seq...) do { \ static const u8 d[] = { seq }; \ int ret; \ ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \ if (ret < 0) \ return ret; \ } while (0) static int xpp055c272_init_sequence(struct xpp055c272 *ctx) { struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); struct device *dev = ctx->dev; /* * Init sequence was supplied by the panel vendor without much * documentation. */ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI, 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01, 0x00, 0x00, 0x37); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF, 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, 0x00); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ, 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER, 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd, 0x67, 0x77, 0x33, 0x33); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff, 0xff, 0x01, 0xff); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09); msleep(20); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1, 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12, 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42, 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58, 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35, 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f, 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88, 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05, 0xa0, 0x00, 0x00, 0x00, 0x00); dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, 0x18); msleep(60); DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); return 0; } static int xpp055c272_unprepare(struct drm_panel *panel) { struct xpp055c272 *ctx = panel_to_xpp055c272(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; if (!ctx->prepared) return 0; ret = mipi_dsi_dcs_set_display_off(dsi); if (ret < 0) DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n", ret); mipi_dsi_dcs_enter_sleep_mode(dsi); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n", ret); return ret; } regulator_disable(ctx->iovcc); regulator_disable(ctx->vci); ctx->prepared = false; return 0; } static int xpp055c272_prepare(struct drm_panel *panel) { struct xpp055c272 *ctx = panel_to_xpp055c272(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; if (ctx->prepared) return 0; DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); ret = regulator_enable(ctx->vci); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "Failed to enable vci supply: %d\n", ret); return ret; } ret = regulator_enable(ctx->iovcc); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); goto disable_vci; } gpiod_set_value_cansleep(ctx->reset_gpio, 1); /* T6: 10us */ usleep_range(10, 20); gpiod_set_value_cansleep(ctx->reset_gpio, 0); /* T8: 20ms */ msleep(20); ret = xpp055c272_init_sequence(ctx); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", ret); goto disable_iovcc; } ret = mipi_dsi_dcs_exit_sleep_mode(dsi); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret); goto disable_iovcc; } /* T9: 120ms */ msleep(120); ret = mipi_dsi_dcs_set_display_on(dsi); if (ret < 0) { DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret); goto disable_iovcc; } msleep(50); ctx->prepared = true; return 0; disable_iovcc: regulator_disable(ctx->iovcc); disable_vci: regulator_disable(ctx->vci); return ret; } static const struct drm_display_mode default_mode = { .hdisplay = 720, .hsync_start = 720 + 40, .hsync_end = 720 + 40 + 10, .htotal = 720 + 40 + 10 + 40, .vdisplay = 1280, .vsync_start = 1280 + 22, .vsync_end = 1280 + 22 + 4, .vtotal = 1280 + 22 + 4 + 11, .vrefresh = 60, .clock = 64000, .width_mm = 68, .height_mm = 121, }; static int xpp055c272_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct xpp055c272 *ctx = panel_to_xpp055c272(panel); struct drm_display_mode *mode; mode = drm_mode_duplicate(connector->dev, &default_mode); if (!mode) { DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", default_mode.hdisplay, default_mode.vdisplay, default_mode.vrefresh); 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 xpp055c272_funcs = { .unprepare = xpp055c272_unprepare, .prepare = xpp055c272_prepare, .get_modes = xpp055c272_get_modes, }; static int xpp055c272_probe(struct mipi_dsi_device *dsi) { struct device *dev = &dsi->dev; struct xpp055c272 *ctx; int ret; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); return PTR_ERR(ctx->reset_gpio); } ctx->vci = devm_regulator_get(dev, "vci"); if (IS_ERR(ctx->vci)) { ret = PTR_ERR(ctx->vci); if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "Failed to request vci regulator: %d\n", ret); return ret; } ctx->iovcc = devm_regulator_get(dev, "iovcc"); if (IS_ERR(ctx->iovcc)) { ret = PTR_ERR(ctx->iovcc); if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "Failed to request iovcc regulator: %d\n", ret); return ret; } mipi_dsi_set_drvdata(dsi, ctx); ctx->dev = dev; dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs, DRM_MODE_CONNECTOR_DSI); ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; drm_panel_add(&ctx->panel); ret = mipi_dsi_attach(dsi); if (ret < 0) { DRM_DEV_ERROR(dev, "mipi_dsi_attach failed: %d\n", ret); drm_panel_remove(&ctx->panel); return ret; } return 0; } static void xpp055c272_shutdown(struct mipi_dsi_device *dsi) { struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); int ret; ret = drm_panel_unprepare(&ctx->panel); if (ret < 0) DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", ret); ret = drm_panel_disable(&ctx->panel); if (ret < 0) DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", ret); } static int xpp055c272_remove(struct mipi_dsi_device *dsi) { struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); int ret; xpp055c272_shutdown(dsi); ret = mipi_dsi_detach(dsi); if (ret < 0) DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); return 0; } static const struct of_device_id xpp055c272_of_match[] = { { .compatible = "xinpeng,xpp055c272" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, xpp055c272_of_match); static struct mipi_dsi_driver xpp055c272_driver = { .driver = { .name = "panel-xinpeng-xpp055c272", .of_match_table = xpp055c272_of_match, }, .probe = xpp055c272_probe, .remove = xpp055c272_remove, .shutdown = xpp055c272_shutdown, }; module_mipi_dsi_driver(xpp055c272_driver); MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1