Hi
Am 02.08.21 um 08:35 schrieb Icenowy Zheng:
Add a driver for generic MIPI DBI panels initialized with MIPI DCS commands.
Currently a ST7789V-based panel is added to it. This panel has its configuration pre-programmed into the controller, so no vendor-specific configuration is needed.
Signed-off-by: Icenowy Zheng icenowy@sipeed.com
drivers/gpu/drm/tiny/Kconfig | 12 ++ drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/simple-dbi.c | 244 ++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 drivers/gpu/drm/tiny/simple-dbi.c
diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index d31be274a2bd..6cfc57b68a46 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -144,6 +144,18 @@ config TINYDRM_REPAPER
If M is selected the module will be called repaper.
+config TINYDRM_SIMPLE_DBI
- tristate "DRM support for generic MIPI DBI panels"
- depends on DRM && SPI
- select DRM_KMS_HELPER
- select DRM_KMS_CMA_HELPER
- select DRM_MIPI_DBI
- help
DRM driver for generic DBI panels that are MIPI DCS compatible
and needs no vendor-specific setup commands.
If M is selected the module will be called simple-dbi.
- config TINYDRM_ST7586 tristate "DRM support for Sitronix ST7586 display panels" depends on DRM && SPI
diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index e09942895c77..2553de651aa3 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o obj-$(CONFIG_TINYDRM_ST7586) += st7586.o obj-$(CONFIG_TINYDRM_ST7735R) += st7735r.o +obj-$(CONFIG_TINYDRM_SIMPLE_DBI) += simple-dbi.o diff --git a/drivers/gpu/drm/tiny/simple-dbi.c b/drivers/gpu/drm/tiny/simple-dbi.c new file mode 100644 index 000000000000..2b207e43d500 --- /dev/null +++ b/drivers/gpu/drm/tiny/simple-dbi.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- DRM driver for display panels with configuration preset and needs only
- standard MIPI DCS commands to bring up.
- Copyright (C) 2021 Sipeed
- */
+#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/dma-buf.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/spi/spi.h> +#include <video/mipi_display.h>
+#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_mipi_dbi.h>
+#define MIPI_DCS_ADDRESS_MODE_BGR BIT(3) +#define MIPI_DCS_ADDRESS_MODE_REVERSE BIT(5) +#define MIPI_DCS_ADDRESS_MODE_RTL BIT(6) +#define MIPI_DCS_ADDRESS_MODE_BTT BIT(7)
+struct simple_dbi_cfg {
- const struct drm_display_mode mode;
- unsigned int left_offset;
- unsigned int top_offset;
- bool inverted;
- bool write_only;
- bool bgr;
- bool right_to_left;
- bool bottom_to_top;
+};
+struct simple_dbi_priv {
- struct mipi_dbi_dev dbidev; /* Must be first for .release() */
Which release?
- const struct simple_dbi_cfg *cfg;
+};
+static void simple_dbi_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
+{
- struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
- struct simple_dbi_priv *priv = container_of(dbidev,
struct simple_dbi_priv,
dbidev);
- struct mipi_dbi *dbi = &dbidev->dbi;
- int ret, idx;
- u8 addr_mode = 0x00;
- if (!drm_dev_enter(pipe->crtc.dev, &idx))
return;
- DRM_DEBUG_KMS("\n");
I'm not a friend of such debugging statements. If you absolutely want to keep it, rather use drm_dbg_kms().
- ret = mipi_dbi_poweron_reset(dbidev);
- if (ret)
goto out_exit;
- mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
- msleep(5);
- /* Currently tinydrm supports 16bit only now */
- mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
MIPI_DCS_PIXEL_FMT_16BIT);
- if (priv->cfg->inverted)
mipi_dbi_command(dbi, MIPI_DCS_ENTER_INVERT_MODE);
- else
mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
- if (priv->cfg->bgr)
addr_mode |= MIPI_DCS_ADDRESS_MODE_BGR;
- if (priv->cfg->right_to_left)
addr_mode |= MIPI_DCS_ADDRESS_MODE_RTL;
- if (priv->cfg->bottom_to_top)
addr_mode |= MIPI_DCS_ADDRESS_MODE_BTT;
- switch (dbidev->rotation) {
- default:
break;
- case 90:
addr_mode ^= MIPI_DCS_ADDRESS_MODE_REVERSE;
addr_mode ^= MIPI_DCS_ADDRESS_MODE_RTL;
break;
- case 180:
addr_mode ^= MIPI_DCS_ADDRESS_MODE_RTL;
addr_mode ^= MIPI_DCS_ADDRESS_MODE_BTT;
break;
- case 270:
addr_mode ^= MIPI_DCS_ADDRESS_MODE_REVERSE;
addr_mode ^= MIPI_DCS_ADDRESS_MODE_BTT;
break;
- }
- mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
- mipi_dbi_command(dbi, MIPI_DCS_ENTER_NORMAL_MODE);
- mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
- mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
+out_exit:
- drm_dev_exit(idx);
+}
+static const struct drm_simple_display_pipe_funcs simple_dbi_pipe_funcs = {
- .enable = simple_dbi_pipe_enable,
- .disable = mipi_dbi_pipe_disable,
- .update = mipi_dbi_pipe_update,
- .prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
+};
+static const struct simple_dbi_cfg zsx154_b1206_cfg = {
- .mode = { DRM_SIMPLE_MODE(240, 240, 28, 28) },
- .inverted = true,
- .write_only = true,
+};
+DEFINE_DRM_GEM_CMA_FOPS(simple_dbi_fops);
+static const struct drm_driver simple_dbi_driver = {
- .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
- .fops = &simple_dbi_fops,
- DRM_GEM_CMA_DRIVER_OPS_VMAP,
- .debugfs_init = mipi_dbi_debugfs_init,
- .name = "simple-dbi",
- .desc = "Generic MIPI-DCS compatible DBI panel",
- .date = "20210723",
- .major = 1,
- .minor = 0,
+};
+static const struct of_device_id simple_dbi_of_match[] = {
- { .compatible = "zsx,zsx154-b1206", .data = &zsx154_b1206_cfg },
- { },
+} +MODULE_DEVICE_TABLE(of, simple_dbi_of_match);
+static int simple_dbi_probe(struct spi_device *spi) +{
- struct device *dev = &spi->dev;
- const struct simple_dbi_cfg *cfg;
- struct mipi_dbi_dev *dbidev;
- struct simple_dbi_priv *priv;
- struct drm_device *drm;
- struct mipi_dbi *dbi;
- struct gpio_desc *dc;
- u32 rotation = 0;
- int ret;
- cfg = device_get_match_data(&spi->dev);
- if (!cfg)
cfg = (void *)spi_get_device_id(spi)->driver_data;
- priv = devm_drm_dev_alloc(dev, &simple_dbi_driver,
struct simple_dbi_priv, dbidev.drm);
- if (IS_ERR(priv))
return PTR_ERR(priv);
- dbidev = &priv->dbidev;
- priv->cfg = cfg;
- dbi = &dbidev->dbi;
- drm = &dbidev->drm;
- dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(dbi->reset))
return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get reset GPIO\n");
- dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
- if (IS_ERR(dc))
return dev_err_probe(dev, PTR_ERR(dc), "Failed to get D/C GPIO\n");
- dbidev->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(dbidev->backlight))
return PTR_ERR(dbidev->backlight);
- device_property_read_u32(dev, "rotation", &rotation);
- ret = mipi_dbi_spi_init(spi, dbi, dc);
- if (ret)
return ret;
- if (cfg->write_only)
dbi->read_commands = NULL;
- ret = mipi_dbi_dev_init(dbidev, &simple_dbi_pipe_funcs, &cfg->mode,
rotation);
- if (ret)
return ret;
- drm_mode_config_reset(drm);
- ret = drm_dev_register(drm, 0);
- if (ret)
return ret;
- spi_set_drvdata(spi, drm);
AFAIK dev files are being created during drm_dev_register(). It's probably safer to set the drvdata before.
- drm_fbdev_generic_setup(drm, 0);
- return 0;
+}
+static int simple_dbi_remove(struct spi_device *spi) +{
- struct drm_device *drm = spi_get_drvdata(spi);
- drm_dev_unplug(drm);
- drm_atomic_helper_shutdown(drm);
- return 0;
+}
+static void simple_dbi_shutdown(struct spi_device *spi) +{
- drm_atomic_helper_shutdown(spi_get_drvdata(spi));
+}
+static struct spi_driver simple_dbi_spi_driver = {
- .driver = {
.name = "simple-dbi",
.of_match_table = simple_dbi_of_match,
- },
- .probe = simple_dbi_probe,
- .remove = simple_dbi_remove,
- .shutdown = simple_dbi_shutdown,
+}; +module_spi_driver(simple_dbi_spi_driver);
+MODULE_DESCRIPTION("Simple DBI panel DRM driver"); +MODULE_AUTHOR("Icenowy Zheng icenowy@aosc.io"); +MODULE_LICENSE("GPL");
Trailing blank line. (?)
Best regards Thomas