[PATCH 2/5] drm/tinydrm: Add tinydrm_panel abstraction

Noralf Trønnes noralf at tronnes.org
Sat Mar 11 21:35:33 UTC 2017


Add support for displays that can be operated using a simple vtable.
Geared towards controllers that can represent it's registers using
regmap.

Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
 Documentation/gpu/tinydrm.rst                |  12 +
 drivers/gpu/drm/tinydrm/Kconfig              |   1 +
 drivers/gpu/drm/tinydrm/core/Makefile        |   2 +-
 drivers/gpu/drm/tinydrm/core/tinydrm-panel.c | 532 +++++++++++++++++++++++++++
 include/drm/tinydrm/tinydrm-panel.h          | 153 ++++++++
 5 files changed, 699 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-panel.c
 create mode 100644 include/drm/tinydrm/tinydrm-panel.h

diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
index a913644..bb78433 100644
--- a/Documentation/gpu/tinydrm.rst
+++ b/Documentation/gpu/tinydrm.rst
@@ -20,6 +20,18 @@ Core functionality
 .. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
    :export:
 
+Panel
+=====
+
+.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-panel.c
+   :doc: overview
+
+.. kernel-doc:: include/drm/tinydrm/tinydrm-panel.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-panel.c
+   :export:
+
 Additional helpers
 ==================
 
diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 3504c53..4ea0fbc 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -1,6 +1,7 @@
 menuconfig DRM_TINYDRM
 	tristate "Support for simple displays"
 	depends on DRM
+	select REGMAP
 	select DRM_KMS_HELPER
 	select DRM_KMS_CMA_HELPER
 	select BACKLIGHT_LCD_SUPPORT
diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
index fb221e6..213b479 100644
--- a/drivers/gpu/drm/tinydrm/core/Makefile
+++ b/drivers/gpu/drm/tinydrm/core/Makefile
@@ -1,3 +1,3 @@
-tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
+tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-panel.o tinydrm-helpers.o
 
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-panel.c b/drivers/gpu/drm/tinydrm/core/tinydrm-panel.c
new file mode 100644
index 0000000..f3e5fb1
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-panel.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2017 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/tinydrm/tinydrm-helpers.h>
+#include <drm/tinydrm/tinydrm-panel.h>
+
+/**
+ * DOC: overview
+ *
+ * This library provides helpers for displays/panels that can be operated
+ * using a simple vtable.
+ *
+ * Many controllers are operated through a register making &regmap a useful
+ * abstraction for the register interface code. This helper is geared towards
+ * such controllers. Often controllers also support more than one bus, and
+ * should for instance a controller be connected in a non-standard way
+ * (e.g. memory mapped), then only the regmap needs to be changed.
+ */
+
+static int tinydrm_panel_prepare(struct tinydrm_panel *panel)
+{
+	if (panel->funcs && panel->funcs->prepare)
+		return panel->funcs->prepare(panel);
+
+	if (panel->regulator)
+		return regulator_enable(panel->regulator);
+
+	return 0;
+}
+
+static int tinydrm_panel_enable(struct tinydrm_panel *panel)
+{
+	if (panel->funcs && panel->funcs->enable)
+		return panel->funcs->enable(panel);
+
+	return tinydrm_enable_backlight(panel->backlight);
+}
+
+static int tinydrm_panel_disable(struct tinydrm_panel *panel)
+{
+	if (panel->funcs && panel->funcs->disable)
+		return panel->funcs->disable(panel);
+
+	return tinydrm_disable_backlight(panel->backlight);
+}
+
+static int tinydrm_panel_unprepare(struct tinydrm_panel *panel)
+{
+	if (panel->funcs && panel->funcs->unprepare)
+		return panel->funcs->unprepare(panel);
+
+	if (panel->regulator)
+		return regulator_disable(panel->regulator);
+
+	return 0;
+}
+
+static void tinydrm_panel_pipe_enable(struct drm_simple_display_pipe *pipe,
+				      struct drm_crtc_state *crtc_state)
+{
+	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
+	struct tinydrm_panel *panel = tinydrm_to_panel(tdev);
+	struct drm_framebuffer *fb = pipe->plane.fb;
+
+	panel->enabled = true;
+	fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0);
+	tinydrm_panel_enable(panel);
+}
+
+static void tinydrm_panel_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
+	struct tinydrm_panel *panel = tinydrm_to_panel(tdev);
+
+	panel->enabled = false;
+	tinydrm_panel_disable(panel);
+}
+
+static void tinydrm_panel_pipe_update(struct drm_simple_display_pipe *pipe,
+				      struct drm_plane_state *old_state)
+{
+	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
+	struct tinydrm_panel *panel = tinydrm_to_panel(tdev);
+	struct drm_framebuffer *fb = pipe->plane.state->fb;
+
+	/* fb is set (not changed) */
+	if (fb && !old_state->fb)
+		tinydrm_panel_prepare(panel);
+
+	tinydrm_display_pipe_update(pipe, old_state);
+
+	/* fb is unset */
+	if (!fb)
+		tinydrm_panel_unprepare(panel);
+}
+
+static const struct drm_simple_display_pipe_funcs tinydrm_panel_pipe_funcs = {
+	.enable = tinydrm_panel_pipe_enable,
+	.disable = tinydrm_panel_pipe_disable,
+	.update = tinydrm_panel_pipe_update,
+	.prepare_fb = tinydrm_display_pipe_prepare_fb,
+};
+
+static int tinydrm_panel_fb_dirty(struct drm_framebuffer *fb,
+				  struct drm_file *file_priv,
+				  unsigned int flags, unsigned int color,
+				  struct drm_clip_rect *clips,
+				  unsigned int num_clips)
+{
+	struct tinydrm_device *tdev = fb->dev->dev_private;
+	struct tinydrm_panel *panel = tinydrm_to_panel(tdev);
+	struct drm_clip_rect rect;
+	int ret = 0;
+
+	if (!panel->funcs || !panel->funcs->flush)
+		return 0;
+
+	mutex_lock(&tdev->dirty_lock);
+
+	if (!panel->enabled)
+		goto out_unlock;
+
+	/* fbdev can flush even when we're not interested */
+	if (tdev->pipe.plane.fb != fb)
+		goto out_unlock;
+
+	tinydrm_merge_clips(&rect, clips, num_clips, flags,
+			    fb->width, fb->height);
+
+	ret = panel->funcs->flush(panel, fb, &rect);
+
+out_unlock:
+	mutex_unlock(&tdev->dirty_lock);
+
+	if (ret)
+		dev_err_once(fb->dev->dev, "Failed to update display %d\n",
+			     ret);
+
+	return ret;
+}
+
+static const struct drm_framebuffer_funcs tinydrm_panel_fb_funcs = {
+	.destroy	= drm_fb_cma_destroy,
+	.create_handle	= drm_fb_cma_create_handle,
+	.dirty		= tinydrm_panel_fb_dirty,
+};
+
+/**
+ * tinydrm_panel_init - Initialize &tinydrm_panel
+ * @dev: Parent device
+ * @panel: &tinydrm_panel structure to initialize
+ * @funcs: Callbacks for the panel (optional)
+ * @formats: Array of supported formats (DRM_FORMAT\_\*). The first format is
+ *           considered the default format and &tinydrm_panel->tx_buf is
+ *           allocated a buffer that can hold a framebuffer with that format.
+ * @format_count: Number of elements in @formats
+ * @driver: DRM driver
+ * @mode: Display mode
+ * @rotation: Initial rotation in degrees Counter Clock Wise
+ *
+ * This function initializes a &tinydrm_panel structure and it's underlying
+ * @tinydrm_device. It also sets up the display pipeline.
+ *
+ * Objects created by this function will be automatically freed on driver
+ * detach (devres).
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int tinydrm_panel_init(struct device *dev, struct tinydrm_panel *panel,
+		       const struct tinydrm_panel_funcs *funcs,
+		       const uint32_t *formats, unsigned int format_count,
+		       struct drm_driver *driver,
+		       const struct drm_display_mode *mode,
+		       unsigned int rotation)
+{
+	struct tinydrm_device *tdev = &panel->tinydrm;
+	const struct drm_format_info *format_info;
+	size_t bufsize;
+	int ret;
+
+	format_info = drm_format_info(formats[0]);
+	bufsize = mode->vdisplay * mode->hdisplay * format_info->cpp[0];
+
+	panel->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
+	if (!panel->tx_buf)
+		return -ENOMEM;
+
+	ret = devm_tinydrm_init(dev, tdev, &tinydrm_panel_fb_funcs, driver);
+	if (ret)
+		return ret;
+
+	ret = tinydrm_display_pipe_init(tdev, &tinydrm_panel_pipe_funcs,
+					DRM_MODE_CONNECTOR_VIRTUAL,
+					formats, format_count, mode,
+					rotation);
+	if (ret)
+		return ret;
+
+	tdev->drm->mode_config.preferred_depth = format_info->depth;
+
+	panel->rotation = rotation;
+	panel->funcs = funcs;
+
+	drm_mode_config_reset(tdev->drm);
+
+	DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
+		      tdev->drm->mode_config.preferred_depth, rotation);
+
+	return 0;
+}
+EXPORT_SYMBOL(tinydrm_panel_init);
+
+/**
+ * tinydrm_panel_rgb565_buf - Return RGB565 buffer to scanout
+ * @panel: tinydrm panel
+ * @fb: DRM framebuffer
+ * @rect: Clip rectangle area to scanout
+ *
+ * This function returns the RGB565 framebuffer rectangle to scanout.
+ * It converts XRGB8888 to RGB565 if necessary.
+ * If copying isn't necessary (RGB565 full rect, no swap), then the backing
+ * CMA buffer is returned.
+ *
+ * Returns:
+ * Buffer to scanout on success, ERR_PTR on failure.
+ */
+void *tinydrm_panel_rgb565_buf(struct tinydrm_panel *panel,
+			       struct drm_framebuffer *fb,
+			       struct drm_clip_rect *rect)
+{
+	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+	bool swap = panel->swap_bytes;
+	bool full;
+	void *buf;
+	int ret;
+
+	full = (rect->x2 - rect->x1) == fb->width &&
+	       (rect->y2 - rect->y1) == fb->height;
+
+	if (panel->always_tx_buf || swap || !full ||
+	    fb->format->format == DRM_FORMAT_XRGB8888) {
+		buf = panel->tx_buf;
+		ret = tinydrm_rgb565_buf_copy(buf, fb, rect, swap);
+		if (ret)
+			return ERR_PTR(ret);
+	} else {
+		buf = cma_obj->vaddr;
+	}
+
+	return buf;
+}
+EXPORT_SYMBOL(tinydrm_panel_rgb565_buf);
+
+/**
+ * tinydrm_panel_pm_suspend - tinydrm_panel PM suspend helper
+ * @dev: Device
+ *
+ * tinydrm_panel drivers can use this in their &device_driver->pm operations.
+ * Use dev_set_drvdata() or similar to set &tinydrm_panel as driver data.
+ */
+int tinydrm_panel_pm_suspend(struct device *dev)
+{
+	struct tinydrm_panel *panel = dev_get_drvdata(dev);
+	int ret;
+
+	ret = tinydrm_suspend(&panel->tinydrm);
+	if (ret)
+		return ret;
+
+	/* fb isn't set to NULL by suspend, do .unprepare() explicitly */
+	tinydrm_panel_unprepare(panel);
+
+	return 0;
+}
+EXPORT_SYMBOL(tinydrm_panel_pm_suspend);
+
+/**
+ * tinydrm_panel_pm_resume - tinydrm_panel PM resume helper
+ * @dev: Device
+ *
+ * tinydrm_panel drivers can use this in their &device_driver->pm operations.
+ * Use dev_set_drvdata() or similar to set &tinydrm_panel as driver data.
+ */
+int tinydrm_panel_pm_resume(struct device *dev)
+{
+	struct tinydrm_panel *panel = dev_get_drvdata(dev);
+
+	/* fb is NULL on resume, .prepare() will be called in pipe_update */
+
+	return tinydrm_resume(&panel->tinydrm);
+}
+EXPORT_SYMBOL(tinydrm_panel_pm_resume);
+
+/**
+ * tinydrm_panel_spi_shutdown - tinydrm_panel SPI shutdown helper
+ * @spi: SPI device
+ *
+ * tinydrm_panel drivers can use this as their shutdown callback to turn off
+ * the display on machine shutdown and reboot. Use spi_set_drvdata() or
+ * similar to set &tinydrm_panel as driver data.
+ */
+void tinydrm_panel_spi_shutdown(struct spi_device *spi)
+{
+	struct tinydrm_panel *panel = spi_get_drvdata(spi);
+
+	tinydrm_shutdown(&panel->tinydrm);
+}
+EXPORT_SYMBOL(tinydrm_panel_spi_shutdown);
+
+/**
+ * tinydrm_panel_i2c_shutdown - tinydrm_panel I2C shutdown helper
+ * @i2c: I2C client device
+ *
+ * tinydrm_panel drivers can use this as their shutdown callback to turn off
+ * the display on machine shutdown and reboot. Use i2c_set_clientdata() or
+ * similar to set &tinydrm_panel as driver data.
+ */
+void tinydrm_panel_i2c_shutdown(struct i2c_client *i2c)
+{
+	struct tinydrm_panel *panel = i2c_get_clientdata(i2c);
+
+	tinydrm_shutdown(&panel->tinydrm);
+}
+EXPORT_SYMBOL(tinydrm_panel_i2c_shutdown);
+
+/**
+ * tinydrm_panel_platform_shutdown - tinydrm_panel platform driver shutdown
+ *                                   helper
+ * @pdev: Platform device
+ *
+ * tinydrm_panel drivers can use this as their shutdown callback to turn off
+ * the display on machine shutdown and reboot. Use platform_set_drvdata() or
+ * similar to set &tinydrm_panel as driver data.
+ */
+void tinydrm_panel_platform_shutdown(struct platform_device *pdev)
+{
+	struct tinydrm_panel *panel = platform_get_drvdata(pdev);
+
+	tinydrm_shutdown(&panel->tinydrm);
+}
+EXPORT_SYMBOL(tinydrm_panel_platform_shutdown);
+
+/**
+ * tinydrm_regmap_raw_swap_bytes - Does a raw write require swapping bytes?
+ * @reg: Regmap
+ *
+ * If the bus doesn't support the full regwidth, it has to break up the word.
+ * Additionally if the bus and machine doesn't match endian wise, this requires
+ * byteswapping the buffer when using regmap_raw_write().
+ *
+ * Returns:
+ * True if byte swapping is needed, otherwise false
+ */
+bool tinydrm_regmap_raw_swap_bytes(struct regmap *reg)
+{
+	int val_bytes = regmap_get_val_bytes(reg);
+	unsigned int bus_val;
+	u16 val16 = 0x00ff;
+
+	if (val_bytes == 1)
+		return false;
+
+	if (WARN_ON_ONCE(val_bytes != 2))
+		return false;
+
+	regmap_parse_val(reg, &val16, &bus_val);
+
+	return val16 != bus_val;
+}
+EXPORT_SYMBOL(tinydrm_regmap_raw_swap_bytes);
+
+#ifdef CONFIG_DEBUG_FS
+
+static int
+tinydrm_kstrtoul_array_from_user(const char __user *s, size_t count,
+				 unsigned int base,
+				 unsigned long *vals, size_t num_vals)
+{
+	char *buf, *pos, *token;
+	int ret, i = 0;
+
+	buf = memdup_user_nul(s, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	pos = buf;
+	while (pos) {
+		if (i == num_vals) {
+			ret = -E2BIG;
+			goto err_free;
+		}
+
+		token = strsep(&pos, " ");
+		if (!token) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+		ret = kstrtoul(token, base, vals++);
+		if (ret < 0)
+			goto err_free;
+		i++;
+	}
+
+err_free:
+	kfree(buf);
+
+	return ret ? ret : i;
+}
+
+static ssize_t tinydrm_regmap_debugfs_reg_write(struct file *file,
+						const char __user *user_buf,
+						size_t count, loff_t *ppos)
+{
+	struct seq_file *m = file->private_data;
+	struct regmap *reg = m->private;
+	unsigned long vals[2];
+	int ret;
+
+	ret = tinydrm_kstrtoul_array_from_user(user_buf, count, 16, vals, 2);
+	if (ret <= 0)
+		return ret;
+
+	if (ret != 2)
+		return -EINVAL;
+
+	ret = regmap_write(reg, vals[0], vals[1]);
+
+	return ret < 0 ? ret : count;
+}
+
+static int tinydrm_regmap_debugfs_reg_show(struct seq_file *m, void *d)
+{
+	struct regmap *reg = m->private;
+	int max_reg = regmap_get_max_register(reg);
+	int val_bytes = regmap_get_val_bytes(reg);
+	unsigned int val;
+	int regnr, ret;
+
+	for (regnr = 0; regnr < max_reg; regnr++) {
+		seq_printf(m, "%.*x: ", val_bytes * 2, regnr);
+		ret = regmap_read(reg, regnr, &val);
+		if (ret)
+			seq_puts(m, "XX\n");
+		else
+			seq_printf(m, "%.*x\n", val_bytes * 2, val);
+	}
+
+	return 0;
+}
+
+static int tinydrm_regmap_debugfs_reg_open(struct inode *inode,
+					   struct file *file)
+{
+	return single_open(file, tinydrm_regmap_debugfs_reg_show,
+			   inode->i_private);
+}
+
+static const struct file_operations tinydrm_regmap_debugfs_reg_fops = {
+	.owner = THIS_MODULE,
+	.open = tinydrm_regmap_debugfs_reg_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = tinydrm_regmap_debugfs_reg_write,
+};
+
+static int
+tinydrm_regmap_debugfs_init(struct regmap *reg, struct dentry *parent)
+{
+	umode_t mode = 0200;
+
+	if (regmap_get_max_register(reg))
+		mode |= 0444;
+
+	debugfs_create_file("registers", mode, parent, reg,
+			    &tinydrm_regmap_debugfs_reg_fops);
+	return 0;
+}
+
+static const struct drm_info_list tinydrm_panel_debugfslist[] = {
+	{ "fb",   drm_fb_cma_debugfs_show, 0 },
+};
+
+/**
+ * tinydrm_panel_debugfs_init - Create tinydrm panel debugfs entries
+ * @minor: DRM minor
+ *
+ * &tinydrm_panel drivers can use this as their
+ * &drm_driver->debugfs_init callback.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int tinydrm_panel_debugfs_init(struct drm_minor *minor)
+{
+	struct tinydrm_device *tdev = minor->dev->dev_private;
+	struct tinydrm_panel *panel = tinydrm_to_panel(tdev);
+	struct regmap *reg = panel->reg;
+	int ret;
+
+	if (reg) {
+		ret = tinydrm_regmap_debugfs_init(reg, minor->debugfs_root);
+		if (ret)
+			return ret;
+	}
+
+	return drm_debugfs_create_files(tinydrm_panel_debugfslist,
+					ARRAY_SIZE(tinydrm_panel_debugfslist),
+					minor->debugfs_root, minor);
+}
+EXPORT_SYMBOL(tinydrm_panel_debugfs_init);
+
+#endif
diff --git a/include/drm/tinydrm/tinydrm-panel.h b/include/drm/tinydrm/tinydrm-panel.h
new file mode 100644
index 0000000..fc4348d
--- /dev/null
+++ b/include/drm/tinydrm/tinydrm-panel.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_TINYDRM_PANEL_H
+#define __LINUX_TINYDRM_PANEL_H
+
+#include <drm/tinydrm/tinydrm.h>
+
+struct backlight_device;
+struct platform_device;
+struct tinydrm_panel;
+struct spi_device;
+struct regulator;
+struct gpio_desc;
+struct regmap;
+
+/**
+ * struct tinydrm_panel_funcs - tinydrm panel functions
+ *
+ * All functions are optional.
+ */
+struct tinydrm_panel_funcs {
+	/**
+	 * @prepare:
+	 *
+	 * Prepare controller/display.
+	 *
+	 * This function is called before framebuffer flushing starts.
+	 * Drivers can use this callback to power on and configure the
+	 * controller/display.
+	 * If this is not set and &tinydrm_panel->regulator is set,
+	 * the regulator is enabled.
+	 */
+	int (*prepare)(struct tinydrm_panel *panel);
+
+	/**
+	 * @enable:
+	 *
+	 * Enable display.
+	 *
+	 * This function is called when the display pipeline is enabled.
+	 * Drivers can use this callback to turn on the display.
+	 * If this is not set and &tinydrm_panel->backlight is set,
+	 * the backlight is turned on.
+	 */
+	int (*enable)(struct tinydrm_panel *panel);
+
+	/**
+	 * @disable:
+	 *
+	 * Disable display.
+	 *
+	 * This function is called when the display pipeline is disabled.
+	 * Drivers can use this callback to turn off the display.
+	 * If this is not set and &tinydrm_panel->backlight is set,
+	 * the backlight is turned off.
+	 */
+	int (*disable)(struct tinydrm_panel *panel);
+
+	/**
+	 * @unprepare:
+	 *
+	 * Unprepare controller/display.
+	 *
+	 * This function is called when framebuffer is unset on the plane.
+	 * Drivers can use this callback to power down the controller/display.
+	 * If this is not set and &tinydrm_panel->regulator is set,
+	 * the regulator is disabled.
+	 */
+	int (*unprepare)(struct tinydrm_panel *panel);
+
+	/**
+	 * @flush:
+	 *
+	 * Flush framebuffer to controller/display.
+	 *
+	 * This function is called when the framebuffer is flushed. This
+	 * happens when userspace calls ioctl DRM_IOCTL_MODE_DIRTYFB, when the
+	 * framebuffer is changed on the plane and when the pipeline is
+	 * enabled. If multiple clip rectangles are passed in, they are merged
+	 * into one rectangle and passed to @flush. No flushing happens
+	 * during the time the pipeline is disabled.
+	 */
+	int (*flush)(struct tinydrm_panel *panel, struct drm_framebuffer *fb,
+		     struct drm_clip_rect *rect);
+};
+
+/**
+ * struct tinydrm_panel - tinydrm panel device
+ * @tinydrm: Base &tinydrm_device
+ * @funcs: tinydrm panel functions (optional)
+ * @reg: Register map (optional)
+ * @enabled: Pipeline is enabled
+ * @tx_buf: Transmit buffer
+ * @swap_bytes: Swap pixel data bytes
+ * @always_tx_buf: Always use @tx_buf
+ * @rotation: Rotation in degrees Counter Clock Wise
+ * @reset: Optional reset gpio
+ * @backlight: Optional backlight device
+ * @regulator: Optional regulator
+ */
+struct tinydrm_panel {
+	struct tinydrm_device tinydrm;
+	const struct tinydrm_panel_funcs *funcs;
+	struct regmap *reg;
+	bool enabled;
+	void *tx_buf;
+	bool swap_bytes;
+	bool always_tx_buf;
+	unsigned int rotation;
+	struct gpio_desc *reset;
+	struct backlight_device *backlight;
+	struct regulator *regulator;
+};
+
+static inline struct tinydrm_panel *
+tinydrm_to_panel(struct tinydrm_device *tdev)
+{
+	return container_of(tdev, struct tinydrm_panel, tinydrm);
+}
+
+int tinydrm_panel_init(struct device *dev, struct tinydrm_panel *panel,
+			const struct tinydrm_panel_funcs *funcs,
+			const uint32_t *formats, unsigned int format_count,
+			struct drm_driver *driver,
+			const struct drm_display_mode *mode,
+			unsigned int rotation);
+
+void *tinydrm_panel_rgb565_buf(struct tinydrm_panel *panel,
+			       struct drm_framebuffer *fb,
+			       struct drm_clip_rect *rect);
+
+int tinydrm_panel_pm_suspend(struct device *dev);
+int tinydrm_panel_pm_resume(struct device *dev);
+void tinydrm_panel_spi_shutdown(struct spi_device *spi);
+void tinydrm_panel_i2c_shutdown(struct i2c_client *i2c);
+void tinydrm_panel_platform_shutdown(struct platform_device *pdev);
+
+bool tinydrm_regmap_raw_swap_bytes(struct regmap *reg);
+
+#ifdef CONFIG_DEBUG_FS
+int tinydrm_panel_debugfs_init(struct drm_minor *minor);
+#else
+#define tinydrm_panel_debugfs_init	NULL
+#endif
+
+#endif /* __LINUX_TINYDRM_PANEL_H */
-- 
2.10.2



More information about the dri-devel mailing list