[PATCH 3/4] video: add MIPI DBI framebuffer helpers

Ahmad Fatoum a.fatoum at pengutronix.de
Thu Mar 30 01:43:54 PDT 2023


On 29.03.23 12:56, Philipp Zabel wrote:
> Port helper functions for the panel-mipi-dbi driver from the Linux
> kernel.
> 
> Signed-off-by: Philipp Zabel <p.zabel at 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);

Do we need this? Why can't we send out framebuffer directly if there is no
swapping to be done?

> +	}
> +}
> +
> +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);

FYI, there's %pe support for printing error codes as string if they
were compiled in.

> +}
> +
> +/**
> + * 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.

fb_ops->fb_enable ?

> + *
> + * 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);

Calling regulator_disable on NULL pointer is a no-op.

> +}
> +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);

dma_alloc? In case some SPI driver gets DMA support.

> +
> +	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);

Use info->screen_size here as well?

> +
> +	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);

returns 0 when called on NULL pointer, so no need for if above.

> +		if (ret) {
> +			dev_err(dev, "Failed to enable regulator (%d)\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (dbidev->io_regulator) {
> +		ret = regulator_enable(dbidev->io_regulator);

Ditto

> +		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);

As above.

> +		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,

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |




More information about the barebox mailing list