mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Philipp Zabel <p.zabel@pengutronix.de>
To: barebox@lists.infradead.org
Subject: [PATCH 3/4] video: add MIPI DBI framebuffer helpers
Date: Wed, 29 Mar 2023 12:56:37 +0200	[thread overview]
Message-ID: <20230329105638.1258096-3-p.zabel@pengutronix.de> (raw)
In-Reply-To: <20230329105638.1258096-1-p.zabel@pengutronix.de>

Port helper functions for the panel-mipi-dbi driver from the Linux
kernel.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
 drivers/video/mipi_dbi.c | 242 +++++++++++++++++++++++++++++++++++++++
 include/video/mipi_dbi.h |  63 ++++++++++
 2 files changed, 305 insertions(+)

diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
index 50d2fc4b29b9..9aa16abb1c9b 100644
--- a/drivers/video/mipi_dbi.c
+++ b/drivers/video/mipi_dbi.c
@@ -13,6 +13,7 @@
 #include <gpiod.h>
 #include <regulator.h>
 #include <spi/spi.h>
+#include <video/backlight.h>
 #include <video/mipi_dbi.h>
 
 #include <video/vpl.h>
@@ -196,6 +197,178 @@ int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
 }
 EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
 
+/**
+ * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
+ * @dst: The destination buffer
+ * @info: The source framebuffer info
+ * @swap: When true, swap MSB/LSB of 16-bit values
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+static void mipi_dbi_buf_copy(void *dst, struct fb_info *info, bool swap)
+{
+	u16 *src = (u16 *)info->screen_base;
+	u16 *dst16 = dst;
+	size_t len = info->xres * info->yres;
+	int i;
+
+	if (swap) {
+		for (i = 0; i < len; i++) {
+			*dst16++ = *src << 8 | *src >> 8;
+			src++;
+		}
+	} else {
+		memcpy(dst, src, len * 2);
+	}
+}
+
+static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev,
+					unsigned int xs, unsigned int xe,
+					unsigned int ys, unsigned int ye)
+{
+	struct mipi_dbi *dbi = &dbidev->dbi;
+
+	xs += dbidev->mode.left_margin;
+	xe += dbidev->mode.left_margin;
+	ys += dbidev->mode.upper_margin;
+	ye += dbidev->mode.upper_margin;
+
+	mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff,
+			 xs & 0xff, (xe >> 8) & 0xff, xe & 0xff);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff,
+			 ys & 0xff, (ye >> 8) & 0xff, ye & 0xff);
+}
+
+static void mipi_dbi_fb_dirty(struct mipi_dbi_dev *dbidev, struct fb_info *info)
+{
+	struct mipi_dbi *dbi = &dbidev->dbi;
+	u16 width = info->xres;
+	u16 height = info->yres;
+	size_t len = width * height * 2;
+	int ret;
+
+	mipi_dbi_buf_copy(dbidev->tx_buf, info, dbi->swap_bytes);
+
+	mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1);
+
+	ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, dbidev->tx_buf, len);
+	if (ret)
+		pr_err_once("Failed to update display %d\n", ret);
+}
+
+/**
+ * mipi_dbi_enable_flush - MIPI DBI enable helper
+ * @dbidev: MIPI DBI device structure
+ * @crtc_state: CRTC state
+ * @plane_state: Plane state
+ *
+ * Flushes the whole framebuffer and enables the backlight. Drivers can use this
+ * in their &drm_simple_display_pipe_funcs->enable callback.
+ *
+ * Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom
+ * framebuffer flushing, can't use this function since they both use the same
+ * flushing code.
+ */
+void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
+			   struct fb_info *info)
+{
+	mipi_dbi_fb_dirty(dbidev, info);
+
+	if (dbidev->backlight)
+		backlight_set_brightness_default(dbidev->backlight);
+}
+EXPORT_SYMBOL(mipi_dbi_enable_flush);
+
+
+static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev)
+{
+	u16 height = dbidev->mode.xres;
+	u16 width = dbidev->mode.yres;
+	struct mipi_dbi *dbi = &dbidev->dbi;
+	size_t len = width * height * 2;
+
+	memset(dbidev->tx_buf, 0, len);
+
+	mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1);
+	mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, dbidev->tx_buf, len);
+}
+
+/**
+ * mipi_dbi_fb_disable - MIPI DBI framebuffer disable helper
+ * @info: Framebuffer info
+ *
+ * This function disables backlight if present, if not the display memory is
+ * blanked. The regulator is disabled if in use. Drivers can use this as their
+ * &fb_ops->fb_disable callback.
+ */
+void mipi_dbi_fb_disable(struct fb_info *info)
+{
+	struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+
+	if (dbidev->backlight)
+		backlight_set_brightness(dbidev->backlight, 0);
+	else
+		mipi_dbi_blank(dbidev);
+
+	if (dbidev->regulator)
+		regulator_disable(dbidev->regulator);
+	if (dbidev->io_regulator)
+		regulator_disable(dbidev->io_regulator);
+}
+EXPORT_SYMBOL(mipi_dbi_fb_disable);
+
+void mipi_dbi_fb_flush(struct fb_info *info)
+{
+	struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info);
+
+	mipi_dbi_fb_dirty(dbidev, info);
+}
+EXPORT_SYMBOL(mipi_dbi_fb_flush);
+
+/**
+ * mipi_dbi_dev_init - MIPI DBI device initialization
+ * @dbidev: MIPI DBI device structure to initialize
+ * @ops: Framebuffer operations
+ * @mode: Display mode
+ *
+ * This function sets up a &fb_info with one fixed &fb_videomode.
+ * Additionally &mipi_dbi.tx_buf is allocated.
+ *
+ * Supported format: RGB565.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, struct fb_ops *ops,
+		      struct fb_videomode *mode)
+{
+	struct fb_info *info = &dbidev->info;
+
+	info->mode = mode;
+	info->fbops = ops;
+	info->dev.parent = dbidev->dev;
+
+	info->xres = mode->xres;
+	info->yres = mode->yres;
+	info->bits_per_pixel = 16;
+	info->line_length = info->xres * (info->bits_per_pixel >> 3);
+
+	info->screen_size = info->line_length * info->yres;
+	info->screen_base = kzalloc(info->screen_size, GFP_KERNEL);
+
+	info->red.length = 5;
+	info->red.offset = 11;
+	info->green.length = 6;
+	info->green.offset = 5;
+	info->blue.length = 5;
+	info->blue.offset = 0;
+
+	dbidev->tx_buf = kzalloc(mode->xres * mode->yres * 2, GFP_KERNEL);
+
+	return 0;
+}
+
 /**
  * mipi_dbi_hw_reset - Hardware reset of controller
  * @dbi: MIPI DBI structure
@@ -246,6 +419,75 @@ bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
 }
 EXPORT_SYMBOL(mipi_dbi_display_is_on);
 
+static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond)
+{
+	struct device *dev = dbidev->dev;
+	struct mipi_dbi *dbi = &dbidev->dbi;
+	int ret;
+
+	if (dbidev->regulator) {
+		ret = regulator_enable(dbidev->regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable regulator (%d)\n", ret);
+			return ret;
+		}
+	}
+
+	if (dbidev->io_regulator) {
+		ret = regulator_enable(dbidev->io_regulator);
+		if (ret) {
+			dev_err(dev, "Failed to enable I/O regulator (%d)\n", ret);
+			if (dbidev->regulator)
+				regulator_disable(dbidev->regulator);
+			return ret;
+		}
+	}
+
+	if (cond && mipi_dbi_display_is_on(dbi))
+		return 1;
+
+	mipi_dbi_hw_reset(dbi);
+	ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET);
+	if (ret) {
+		dev_err(dev, "Failed to send reset command (%d)\n", ret);
+		if (dbidev->regulator)
+			regulator_disable(dbidev->regulator);
+		if (dbidev->io_regulator)
+			regulator_disable(dbidev->io_regulator);
+		return ret;
+	}
+
+	/*
+	 * If we did a hw reset, we know the controller is in Sleep mode and
+	 * per MIPI DSC spec should wait 5ms after soft reset. If we didn't,
+	 * we assume worst case and wait 120ms.
+	 */
+	if (dbi->reset)
+		mdelay(5);
+	else
+		mdelay(120);
+
+	return 0;
+}
+
+/**
+ * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset
+ * @dbidev: MIPI DBI device structure
+ *
+ * This function enables the regulator if used and if the display is off, it
+ * does a hardware and software reset. If mipi_dbi_display_is_on() determines
+ * that the display is on, no reset is performed.
+ *
+ * Returns:
+ * Zero if the controller was reset, 1 if the display was already on, or a
+ * negative error code.
+ */
+int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev)
+{
+	return mipi_dbi_poweron_reset_conditional(dbidev, true);
+}
+EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset);
+
 #if IS_ENABLED(CONFIG_SPI)
 
 /**
diff --git a/include/video/mipi_dbi.h b/include/video/mipi_dbi.h
index 54526006935f..917f7ddd597f 100644
--- a/include/video/mipi_dbi.h
+++ b/include/video/mipi_dbi.h
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 #include <spi/spi.h>
 #include <driver.h>
+#include <fb.h>
 
 struct regulator;
 struct fb_videomode;
@@ -55,6 +56,61 @@ struct mipi_dbi {
 	struct list_head list;
 };
 
+/**
+ * struct mipi_dbi_dev - MIPI DBI device
+ */
+struct mipi_dbi_dev {
+	/**
+	 * @dev: Device
+	 */
+	struct device *dev;
+
+	/**
+	 * @info: Framebuffer info
+	 */
+	struct fb_info info;
+
+	/**
+	 * @mode: Fixed display mode
+	 */
+	struct fb_videomode mode;
+
+	/**
+	 * @tx_buf: Buffer used for transfer (copy clip rect area)
+	 */
+	u8 *tx_buf;
+
+	/**
+	 * @backlight_node: backlight device node (optional)
+	 */
+	struct device_node *backlight_node;
+
+	/**
+	 * @backlight: backlight device (optional)
+	 */
+	struct backlight_device *backlight;
+
+	/**
+	 * @regulator: power regulator (Vdd) (optional)
+	 */
+	struct regulator *regulator;
+
+	/**
+	 * @io_regulator: I/O power regulator (Vddi) (optional)
+	 */
+	struct regulator *io_regulator;
+
+	/**
+	 * @dbi: MIPI DBI interface
+	 */
+	struct mipi_dbi dbi;
+
+	/**
+	 * @driver_private: Driver private data.
+	 */
+	void *driver_private;
+};
+
 static inline const char *mipi_dbi_name(struct mipi_dbi *dbi)
 {
 	return dev_name(&dbi->spi->dev);
@@ -62,8 +118,15 @@ static inline const char *mipi_dbi_name(struct mipi_dbi *dbi)
 
 int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
 		      int dc);
+int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev,
+		      struct fb_ops *ops, struct fb_videomode *mode);
+void mipi_dbi_fb_flush(struct fb_info *info);
+void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
+			   struct fb_info *info);
+void mipi_dbi_fb_disable(struct fb_info *info);
 void mipi_dbi_hw_reset(struct mipi_dbi *dbi);
 bool mipi_dbi_display_is_on(struct mipi_dbi *dbi);
+int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev);
 
 u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len);
 int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
-- 
2.39.2




  parent reply	other threads:[~2023-03-29 10:58 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-29 10:56 [PATCH 1/4] firmware: Add request/release_firmware() calls Philipp Zabel
2023-03-29 10:56 ` [PATCH 2/4] video: Add of_get_display_timing Philipp Zabel
2023-03-30  8:32   ` Ahmad Fatoum
2023-03-29 10:56 ` Philipp Zabel [this message]
2023-03-30  8:43   ` [PATCH 3/4] video: add MIPI DBI framebuffer helpers Ahmad Fatoum
2023-03-30 12:02     ` Philipp Zabel
2023-03-29 10:56 ` [PATCH 4/4] video: Add MIPI DBI compatible SPI driver Philipp Zabel
2023-03-30  8:56   ` Ahmad Fatoum
2023-03-30  8:30 ` [PATCH 1/4] firmware: Add request/release_firmware() calls Ahmad Fatoum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230329105638.1258096-3-p.zabel@pengutronix.de \
    --to=p.zabel@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox