diff options
author | Dave Airlie <airlied@redhat.com> | 2020-11-27 09:23:36 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2020-11-27 09:36:33 +1000 |
commit | 22f8c80566c4a29a0d8b5ebf24aa1fd1679b39e5 (patch) | |
tree | d3d47389e5c9a798f346f9a410d0968372ec941d /drivers/gpu/drm/panel | |
parent | 31b05212360cbf3af3c2e1b7f42e176e0eebedb5 (diff) | |
parent | fa388231fec99b60346319d56495ae531b666275 (diff) |
Merge tag 'drm-misc-next-2020-11-18' of ssh://git.freedesktop.org/git/drm/drm-misc into drm-next
drm-misc-next for 5.11:
UAPI Changes:
* media: Add MEDIA_BUS_FMT_RGB888_3X8_DELTA format
Cross-subsystem Changes:
* console: Remove unused functions; Store characters-per-font in font-
descriptor structure instead of hard-coding
* DT: Add vendor prefix for ShenZhen Asia Better Technology Ltd. (ABT)
Core Changes:
* Fix build warnings
* Update debug logging to new interfaces, plus fixes
* Add error messages for ioctls;
* Fix kernel docs
* doc: Fix kernel docs
* fbcon: Remove accelerated scrolling
* selftests: Fix build warnings
* ttm: Fix missing NULL check in new page pool; Fix build warnings
* video: Fix kernel docs
Driver Changes:
* armada: Fix build warnings
* atmel-hlcdc: Fix build warnings
* exynos: Fix build warnings
* gma500: Remove 2d framebuffer acceleration
* lima: Fix build warnings; Cleanups
* mediatek: Fix build warnings
* meson: Module removal fixes; Fix build warnings
* nouveau: Fix build warnings
* omap: Fix return values
* panel: Fix build warnings; Add support and DT bindings for OnePlus 6/T; Add
support and DT bindings for ABT Y030XX067A
* panel/s6e63m0: Add/improve SPi reading/writing; Support 3WIRE protocol; Set
connector display info; Add more comments
* panfrost: Move GPU reset into separate worker, avoid race conditions
* pl111: Fix build warnings
* qxl: Cleanup fbcon acceleration
* rockchip: Fix build warnings
* savage: Fix build warnings
* sti: Fix build warnings
* udl: Fix missing error code in udl_handle_damage()
* v3d: Fix build warnings
* vc4: Fix build warnings
* via: Fix build warnings
* virtio: Make dma-buf ops static
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20201118123221.GA19755@linux-uq9g
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-abt-y030xx067a.c | 363 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e63m0.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-sofef00.c | 351 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-tpo-tpg110.c | 3 |
7 files changed, 772 insertions, 17 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index e386524b2d77..b4e021ea30f9 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -8,6 +8,15 @@ config DRM_PANEL menu "Display Panels" depends on DRM && DRM_PANEL +config DRM_PANEL_ABT_Y030XX067A + tristate "ABT Y030XX067A 320x480 LCD panel" + depends on OF && SPI + select REGMAP_SPI + help + Say Y here to enable support for the Asia Better Technology Ltd. + Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300 + and RG-99 handheld gaming consoles. + config DRM_PANEL_ARM_VERSATILE tristate "ARM Versatile panel driver" depends on OF @@ -371,6 +380,18 @@ config DRM_PANEL_SAMSUNG_S6E8AA0 select DRM_MIPI_DSI select VIDEOMODE_HELPERS +config DRM_PANEL_SAMSUNG_SOFEF00 + tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS + help + Say Y or M here if you want to enable support for the Samsung AMOLED + command mode panels found in the OnePlus 6/6T smartphones. + + The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively + config DRM_PANEL_SEIKO_43WVF1G tristate "Seiko 43WVF1G panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index d1f8cc572f37..ebbf488c7eac 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o @@ -39,6 +40,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o diff --git a/drivers/gpu/drm/panel/panel-abt-y030xx067a.c b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c new file mode 100644 index 000000000000..2d8794d495d0 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-abt-y030xx067a.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Asia Better Technology Ltd. Y030XX067A IPS LCD panel driver + * + * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net> + * Copyright (C) 2020, Christophe Branchereau <cbranchereau@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define REG00_VBRT_CTRL(val) (val) + +#define REG01_COM_DC(val) (val) + +#define REG02_DA_CONTRAST(val) (val) +#define REG02_VESA_SEL(val) ((val) << 5) +#define REG02_COMDC_SW BIT(7) + +#define REG03_VPOSITION(val) (val) +#define REG03_BSMOUNT BIT(5) +#define REG03_COMTST BIT(6) +#define REG03_HPOSITION1 BIT(7) + +#define REG04_HPOSITION1(val) (val) + +#define REG05_CLIP BIT(0) +#define REG05_NVM_VREFRESH BIT(1) +#define REG05_SLFR BIT(2) +#define REG05_SLBRCHARGE(val) ((val) << 3) +#define REG05_PRECHARGE_LEVEL(val) ((val) << 6) + +#define REG06_TEST5 BIT(0) +#define REG06_SLDWN BIT(1) +#define REG06_SLRGT BIT(2) +#define REG06_TEST2 BIT(3) +#define REG06_XPSAVE BIT(4) +#define REG06_GAMMA_SEL(val) ((val) << 5) +#define REG06_NT BIT(7) + +#define REG07_TEST1 BIT(0) +#define REG07_HDVD_POL BIT(1) +#define REG07_CK_POL BIT(2) +#define REG07_TEST3 BIT(3) +#define REG07_TEST4 BIT(4) +#define REG07_480_LINEMASK BIT(5) +#define REG07_AMPTST(val) ((val) << 6) + +#define REG08_SLHRC(val) (val) +#define REG08_CLOCK_DIV(val) ((val) << 2) +#define REG08_PANEL(val) ((val) << 5) + +#define REG09_SUB_BRIGHT_R(val) (val) +#define REG09_NW_NB BIT(6) +#define REG09_IPCON BIT(7) + +#define REG0A_SUB_BRIGHT_B(val) (val) +#define REG0A_PAIR BIT(6) +#define REG0A_DE_SEL BIT(7) + +#define REG0B_MBK_POSITION(val) (val) +#define REG0B_HD_FREERUN BIT(4) +#define REG0B_VD_FREERUN BIT(5) +#define REG0B_YUV2BIN(val) ((val) << 6) + +#define REG0C_CONTRAST_R(val) (val) +#define REG0C_DOUBLEREAD BIT(7) + +#define REG0D_CONTRAST_G(val) (val) +#define REG0D_RGB_YUV BIT(7) + +#define REG0E_CONTRAST_B(val) (val) +#define REG0E_PIXELCOLORDRIVE BIT(7) + +#define REG0F_ASPECT BIT(0) +#define REG0F_OVERSCAN(val) ((val) << 1) +#define REG0F_FRAMEWIDTH(val) ((val) << 3) + +#define REG10_BRIGHT(val) (val) + +#define REG11_SIG_GAIN(val) (val) +#define REG11_SIGC_CNTL BIT(6) +#define REG11_SIGC_POL BIT(7) + +#define REG12_COLOR(val) (val) +#define REG12_PWCKSEL(val) ((val) << 6) + +#define REG13_4096LEVEL_CNTL(val) (val) +#define REG13_SL4096(val) ((val) << 4) +#define REG13_LIMITER_CONTROL BIT(7) + +#define REG14_PANEL_TEST(val) (val) + +#define REG15_NVM_LINK0 BIT(0) +#define REG15_NVM_LINK1 BIT(1) +#define REG15_NVM_LINK2 BIT(2) +#define REG15_NVM_LINK3 BIT(3) +#define REG15_NVM_LINK4 BIT(4) +#define REG15_NVM_LINK5 BIT(5) +#define REG15_NVM_LINK6 BIT(6) +#define REG15_NVM_LINK7 BIT(7) + +struct y030xx067a_info { + const struct drm_display_mode *display_modes; + unsigned int num_modes; + u16 width_mm, height_mm; + u32 bus_format, bus_flags; +}; + +struct y030xx067a { + struct drm_panel panel; + struct spi_device *spi; + struct regmap *map; + + const struct y030xx067a_info *panel_info; + + struct regulator *supply; + struct gpio_desc *reset_gpio; +}; + +static inline struct y030xx067a *to_y030xx067a(struct drm_panel *panel) +{ + return container_of(panel, struct y030xx067a, panel); +} + +static const struct reg_sequence y030xx067a_init_sequence[] = { + { 0x00, REG00_VBRT_CTRL(0x7f) }, + { 0x01, REG01_COM_DC(0x3c) }, + { 0x02, REG02_VESA_SEL(0x3) | REG02_DA_CONTRAST(0x1f) }, + { 0x03, REG03_VPOSITION(0x0a) }, + { 0x04, REG04_HPOSITION1(0xd2) }, + { 0x05, REG05_CLIP | REG05_NVM_VREFRESH | REG05_SLBRCHARGE(0x2) }, + { 0x06, REG06_XPSAVE | REG06_NT }, + { 0x07, 0 }, + { 0x08, REG08_PANEL(0x1) | REG08_CLOCK_DIV(0x2) }, + { 0x09, REG09_SUB_BRIGHT_R(0x20) }, + { 0x0a, REG0A_SUB_BRIGHT_B(0x20) }, + { 0x0b, REG0B_HD_FREERUN | REG0B_VD_FREERUN }, + { 0x0c, REG0C_CONTRAST_R(0x10) }, + { 0x0d, REG0D_CONTRAST_G(0x10) }, + { 0x0e, REG0E_CONTRAST_B(0x10) }, + { 0x0f, 0 }, + { 0x10, REG10_BRIGHT(0x7f) }, + { 0x11, REG11_SIGC_CNTL | REG11_SIG_GAIN(0x3f) }, + { 0x12, REG12_COLOR(0x20) | REG12_PWCKSEL(0x1) }, + { 0x13, REG13_4096LEVEL_CNTL(0x8) }, + { 0x14, 0 }, + { 0x15, 0 }, +}; + +static int y030xx067a_prepare(struct drm_panel *panel) +{ + struct y030xx067a *priv = to_y030xx067a(panel); + struct device *dev = &priv->spi->dev; + int err; + + err = regulator_enable(priv->supply); + if (err) { + dev_err(dev, "Failed to enable power supply: %d\n", err); + return err; + } + + /* Reset the chip */ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(1000, 20000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(1000, 20000); + + err = regmap_multi_reg_write(priv->map, y030xx067a_init_sequence, + ARRAY_SIZE(y030xx067a_init_sequence)); + if (err) { + dev_err(dev, "Failed to init registers: %d\n", err); + goto err_disable_regulator; + } + + msleep(120); + + return 0; + +err_disable_regulator: + regulator_disable(priv->supply); + return err; +} + +static int y030xx067a_unprepare(struct drm_panel *panel) +{ + struct y030xx067a *priv = to_y030xx067a(panel); + + gpiod_set_value_cansleep(priv->reset_gpio, 1); + regulator_disable(priv->supply); + + return 0; +} + +static int y030xx067a_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct y030xx067a *priv = to_y030xx067a(panel); + const struct y030xx067a_info *panel_info = priv->panel_info; + struct drm_display_mode *mode; + unsigned int i; + + for (i = 0; i < panel_info->num_modes; i++) { + mode = drm_mode_duplicate(connector->dev, + &panel_info->display_modes[i]); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + if (panel_info->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + + drm_display_info_set_bus_formats(&connector->display_info, + &panel_info->bus_format, 1); + connector->display_info.bus_flags = panel_info->bus_flags; + + return panel_info->num_modes; +} + +static const struct drm_panel_funcs y030xx067a_funcs = { + .prepare = y030xx067a_prepare, + .unprepare = y030xx067a_unprepare, + .get_modes = y030xx067a_get_modes, +}; + +static const struct regmap_config y030xx067a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x15, +}; + +static int y030xx067a_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct y030xx067a *priv; + int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spi = spi; + spi_set_drvdata(spi, priv); + + priv->map = devm_regmap_init_spi(spi, &y030xx067a_regmap_config); + if (IS_ERR(priv->map)) { + dev_err(dev, "Unable to init regmap\n"); + return PTR_ERR(priv->map); + } + + priv->panel_info = of_device_get_match_data(dev); + if (!priv->panel_info) + return -EINVAL; + + priv->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(priv->supply)) { + dev_err(dev, "Failed to get power supply\n"); + return PTR_ERR(priv->supply); + } + + priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "Failed to get reset GPIO\n"); + return PTR_ERR(priv->reset_gpio); + } + + drm_panel_init(&priv->panel, dev, &y030xx067a_funcs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&priv->panel); + if (err) + return err; + + drm_panel_add(&priv->panel); + + return 0; +} + +static int y030xx067a_remove(struct spi_device *spi) +{ + struct y030xx067a *priv = spi_get_drvdata(spi); + + drm_panel_remove(&priv->panel); + drm_panel_disable(&priv->panel); + drm_panel_unprepare(&priv->panel); + + return 0; +} + +static const struct drm_display_mode y030xx067a_modes[] = { + { /* 60 Hz */ + .clock = 14400, + .hdisplay = 320, + .hsync_start = 320 + 10, + .hsync_end = 320 + 10 + 37, + .htotal = 320 + 10 + 37 + 33, + .vdisplay = 480, + .vsync_start = 480 + 84, + .vsync_end = 480 + 84 + 20, + .vtotal = 480 + 84 + 20 + 16, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + { /* 50 Hz */ + .clock = 12000, + .hdisplay = 320, + .hsync_start = 320 + 10, + .hsync_end = 320 + 10 + 37, + .htotal = 320 + 10 + 37 + 33, + .vdisplay = 480, + .vsync_start = 480 + 84, + .vsync_end = 480 + 84 + 20, + .vtotal = 480 + 84 + 20 + 16, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct y030xx067a_info y030xx067a_info = { + .display_modes = y030xx067a_modes, + .num_modes = ARRAY_SIZE(y030xx067a_modes), + .width_mm = 69, + .height_mm = 51, + .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_LOW, +}; + +static const struct of_device_id y030xx067a_of_match[] = { + { .compatible = "abt,y030xx067a", .data = &y030xx067a_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, y030xx067a_of_match); + +static struct spi_driver y030xx067a_driver = { + .driver = { + .name = "abt-y030xx067a", + .of_match_table = y030xx067a_of_match, + }, + .probe = y030xx067a_probe, + .remove = y030xx067a_remove, +}; +module_spi_driver(y030xx067a_driver); + +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c index d298d780220d..326deb3177b6 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-spi.c @@ -13,28 +13,28 @@ static int s6e63m0_spi_dcs_read(struct device *dev, const u8 cmd, u8 *data) { - /* - * FIXME: implement reading DCS commands over SPI so we can - * properly identify which physical panel is connected. - */ - *data = 0; + struct spi_device *spi = to_spi_device(dev); + u16 buf[1]; + u16 rbuf[1]; + int ret; + + /* SPI buffers are always in CPU order */ + buf[0] = (u16)cmd; + ret = spi_write_then_read(spi, buf, 2, rbuf, 2); + dev_dbg(dev, "READ CMD: %04x RET: %04x\n", buf[0], rbuf[0]); + if (!ret) + /* These high 8 bits of the 9 contains the readout */ + *data = (rbuf[0] & 0x1ff) >> 1; - return 0; + return ret; } static int s6e63m0_spi_write_word(struct device *dev, u16 data) { struct spi_device *spi = to_spi_device(dev); - struct spi_transfer xfer = { - .len = 2, - .tx_buf = &data, - }; - struct spi_message msg; - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - return spi_sync(spi, &msg); + /* SPI buffers are always in CPU order */ + return spi_write(spi, &data, 2); } static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len) @@ -42,10 +42,17 @@ static int s6e63m0_spi_dcs_write(struct device *dev, const u8 *data, size_t len) int ret = 0; dev_dbg(dev, "SPI writing dcs seq: %*ph\n", (int)len, data); + + /* + * This sends 9 bits with the first bit (bit 8) set to 0 + * This indicates that this is a command. Anything after the + * command is data. + */ ret = s6e63m0_spi_write_word(dev, *data); while (!ret && --len) { ++data; + /* This sends 9 bits with the first bit (bit 8) set to 1 */ ret = s6e63m0_spi_write_word(dev, *data | DATA_MASK); } @@ -65,7 +72,8 @@ static int s6e63m0_spi_probe(struct spi_device *spi) int ret; spi->bits_per_word = 9; - spi->mode = SPI_MODE_3; + /* Preserve e.g. SPI_3WIRE setting */ + spi->mode |= SPI_MODE_3; ret = spi_setup(spi); if (ret < 0) { dev_err(dev, "spi setup failed.\n"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index 3eee67e2d86a..210e70da3a15 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -16,6 +16,7 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/regulator/consumer.h> +#include <linux/media-bus-format.h> #include <video/mipi_display.h> @@ -410,6 +411,7 @@ static int s6e63m0_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct drm_display_mode *mode; + static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; mode = drm_mode_duplicate(connector->dev, &default_mode); if (!mode) { @@ -419,6 +421,13 @@ static int s6e63m0_get_modes(struct drm_panel *panel, return -ENOMEM; } + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_display_info_set_bus_formats(&connector->display_info, + &bus_format, 1); + connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + drm_mode_set_name(mode); mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c new file mode 100644 index 000000000000..8cb1853574bb --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020 Caleb Connolly <caleb@connolly.tech> + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/swab.h> +#include <linux/backlight.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +struct sofef00_panel { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator *supply; + struct gpio_desc *reset_gpio; + const struct drm_display_mode *mode; + bool prepared; +}; + +static inline +struct sofef00_panel *to_sofef00_panel(struct drm_panel *panel) +{ + return container_of(panel, struct sofef00_panel, panel); +} + +#define dsi_dcs_write_seq(dsi, seq...) do { \ + static const u8 d[] = { seq }; \ + int ret; \ + ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \ + if (ret < 0) \ + return ret; \ + } while (0) + +static void sofef00_panel_reset(struct sofef00_panel *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(2000, 3000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(12000, 13000); +} + +static int sofef00_panel_on(struct sofef00_panel *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + usleep_range(10000, 11000); + + dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a); + + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) { + dev_err(dev, "Failed to set tear on: %d\n", ret); + return ret; + } + + dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5); + dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a); + dsi_dcs_write_seq(dsi, 0xb0, 0x07); + dsi_dcs_write_seq(dsi, 0xb6, 0x12); + dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5); + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); + dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display on: %d\n", ret); + return ret; + } + + return 0; +} + +static int sofef00_panel_off(struct sofef00_panel *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(40); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(160); + + return 0; +} + +static int sofef00_panel_prepare(struct drm_panel *panel) +{ + struct sofef00_panel *ctx = to_sofef00_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_enable(ctx->supply); + if (ret < 0) { + dev_err(dev, "Failed to enable regulator: %d\n", ret); + return ret; + } + + sofef00_panel_reset(ctx); + + ret = sofef00_panel_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int sofef00_panel_unprepare(struct drm_panel *panel) +{ + struct sofef00_panel *ctx = to_sofef00_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = sofef00_panel_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + regulator_disable(ctx->supply); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode enchilada_panel_mode = { + .clock = (1080 + 112 + 16 + 36) * (2280 + 36 + 8 + 12) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 112, + .hsync_end = 1080 + 112 + 16, + .htotal = 1080 + 112 + 16 + 36, + .vdisplay = 2280, + .vsync_start = 2280 + 36, + .vsync_end = 2280 + 36 + 8, + .vtotal = 2280 + 36 + 8 + 12, + .width_mm = 68, + .height_mm = 145, +}; + +static const struct drm_display_mode fajita_panel_mode = { + .clock = (1080 + 72 + 16 + 36) * (2340 + 32 + 4 + 18) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 72, + .hsync_end = 1080 + 72 + 16, + .htotal = 1080 + 72 + 16 + 36, + .vdisplay = 2340, + .vsync_start = 2340 + 32, + .vsync_end = 2340 + 32 + 4, + .vtotal = 2340 + 32 + 4 + 18, + .width_mm = 68, + .height_mm = 145, +}; + +static int sofef00_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector) +{ + struct drm_display_mode *mode; + struct sofef00_panel *ctx = to_sofef00_panel(panel); + + mode = drm_mode_duplicate(connector->dev, ctx->mode); + if (!mode) + 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 sofef00_panel_panel_funcs = { + .prepare = sofef00_panel_prepare, + .unprepare = sofef00_panel_unprepare, + .get_modes = sofef00_panel_get_modes, +}; + +static int sofef00_panel_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + int err; + u16 brightness; + + brightness = (u16)backlight_get_brightness(bl); + // This panel needs the high and low bytes swapped for the brightness value + brightness = __swab16(brightness); + + err = mipi_dsi_dcs_set_display_brightness(dsi, brightness); + if (err < 0) + return err; + + return 0; +} + +static const struct backlight_ops sofef00_panel_bl_ops = { + .update_status = sofef00_panel_bl_update_status, +}; + +static struct backlight_device * +sofef00_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_PLATFORM, + .brightness = 1023, + .max_brightness = 1023, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &sofef00_panel_bl_ops, &props); +} + +static int sofef00_panel_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct sofef00_panel *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->mode = of_device_get_match_data(dev); + + if (!ctx->mode) { + dev_err(dev, "Missing device mode\n"); + return -ENODEV; + } + + ctx->supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(ctx->supply)) { + ret = PTR_ERR(ctx->supply); + dev_err(dev, "Failed to get vddio regulator: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) { + ret = PTR_ERR(ctx->reset_gpio); + dev_warn(dev, "Failed to get reset-gpios: %d\n", ret); + return ret; + } + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + + drm_panel_init(&ctx->panel, dev, &sofef00_panel_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ctx->panel.backlight = sofef00_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + return ret; + } + + return 0; +} + +static int sofef00_panel_remove(struct mipi_dsi_device *dsi) +{ + struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); + + return 0; +} + +static const struct of_device_id sofef00_panel_of_match[] = { + { // OnePlus 6 / enchilada + .compatible = "samsung,sofef00", + .data = &enchilada_panel_mode, + }, + { // OnePlus 6T / fajita + .compatible = "samsung,s6e3fc2x01", + .data = &fajita_panel_mode, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sofef00_panel_of_match); + +static struct mipi_dsi_driver sofef00_panel_driver = { + .probe = sofef00_panel_probe, + .remove = sofef00_panel_remove, + .driver = { + .name = "panel-oneplus6", + .of_match_table = sofef00_panel_of_match, + }, +}; + +module_mipi_dsi_driver(sofef00_panel_driver); + +MODULE_AUTHOR("Caleb Connolly <caleb@connolly.tech>"); +MODULE_DESCRIPTION("DRM driver for Samsung AMOLED DSI panels found in OnePlus 6/6T phones"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-tpo-tpg110.c b/drivers/gpu/drm/panel/panel-tpo-tpg110.c index d57ed75a977c..e3791dad6830 100644 --- a/drivers/gpu/drm/panel/panel-tpo-tpg110.c +++ b/drivers/gpu/drm/panel/panel-tpo-tpg110.c @@ -76,7 +76,7 @@ struct tpg110 { */ struct drm_panel panel; /** - * @panel_type: the panel mode as detected + * @panel_mode: the panel mode as detected */ const struct tpg110_panel_mode *panel_mode; /** @@ -362,6 +362,7 @@ static int tpg110_enable(struct drm_panel *panel) /** * tpg110_get_modes() - return the appropriate mode * @panel: the panel to get the mode for + * @connector: reference to the central DRM connector control structure * * This currently does not present a forest of modes, instead it * presents the mode that is configured for the system under use, |