summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2022-02-02 23:30:22 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2022-02-08 09:21:16 +0100
commita046ff2df85bbab113ed3f78d94d5f957a16df7c (patch)
treed5f17b66723a729119abb53b895676cce87c82bf
parent4f0a92ecb0358c428cc268a22e7925e8e828840a (diff)
downloadbarebox-a046ff2df85bbab113ed3f78d94d5f957a16df7c.tar.gz
barebox-a046ff2df85bbab113ed3f78d94d5f957a16df7c.tar.xz
video: add MIPI DBI Type C Option 3 support
Import the Linux v5.15 state of the driver to allow easy porting of MIPI-DBI displays like the Ilitek 9431 added in a follow-up commit. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Link: https://lore.barebox.org/20220202223023.341817-4-a.fatoum@pengutronix.de Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--commands/Kconfig15
-rw-r--r--commands/Makefile1
-rw-r--r--commands/mipi_dbi.c104
-rw-r--r--drivers/video/Kconfig3
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/mipi_dbi.c467
-rw-r--r--include/spi/spi.h20
-rw-r--r--include/video/mipi_dbi.h105
-rw-r--r--include/video/mipi_display.h150
9 files changed, 866 insertions, 0 deletions
diff --git a/commands/Kconfig b/commands/Kconfig
index ba8ca5cdeb..caef1e8fb5 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1969,6 +1969,21 @@ config CMD_SPI
-w BIT bits per word (default 8)
-v verbose
+config CMD_MIPI_DBI
+ bool
+ depends on DRIVER_VIDEO_MIPI_DBI && SPI
+ select PRINTF_HEXSTR
+ prompt "mipi_dbi command"
+ help
+ write/read from MIPI DBI SPI device
+
+ Usage: mipi_dbi [-wld] [REG] [DATA...]
+
+ Options:
+ -l list all MIPI DBI devices
+ -d DEVICE select specific device (default is first registered)
+ -w issue write command
+
config CMD_LED_TRIGGER
bool
depends on LED_TRIGGERS
diff --git a/commands/Makefile b/commands/Makefile
index db78d0b877..fffb6d979e 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_GPIO) += gpio.o
obj-$(CONFIG_CMD_UNCOMPRESS) += uncompress.o
obj-$(CONFIG_CMD_I2C) += i2c.o
obj-$(CONFIG_CMD_SPI) += spi.o
+obj-$(CONFIG_CMD_MIPI_DBI) += mipi_dbi.o
obj-$(CONFIG_CMD_UBI) += ubi.o
obj-$(CONFIG_CMD_UBIFORMAT) += ubiformat.o
obj-$(CONFIG_CMD_MENU) += menu.o
diff --git a/commands/mipi_dbi.c b/commands/mipi_dbi.c
new file mode 100644
index 0000000000..b9b665b721
--- /dev/null
+++ b/commands/mipi_dbi.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: © 2022 Ahmad Fatoum
+
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <video/mipi_dbi.h>
+#include <video/mipi_display.h>
+
+static int mipi_dbi_command_show(struct mipi_dbi *dbi, int cmd)
+{
+ u8 val[4];
+ int ret;
+ size_t len;
+
+ if (!mipi_dbi_command_is_read(dbi, cmd))
+ return -EACCES;
+
+ len = mipi_dbi_command_read_len(cmd);
+
+ printf("%02x: ", cmd);
+ ret = mipi_dbi_command_buf(dbi, cmd, val, len);
+ if (ret) {
+ printf("XX\n");
+ return ret;
+ }
+ printf("%*phN\n", (int)len, val);
+
+ return 0;
+}
+
+static int do_mipi_dbi(int argc, char *argv[])
+{
+ struct mipi_dbi *dbi;
+ int opt, ret, i;
+ bool write = false;
+ u8 cmd, val[4];
+
+ dbi = list_first_entry_or_null(&mipi_dbi_list, struct mipi_dbi, list);
+
+ while ((opt = getopt(argc, argv, "wld:")) > 0) {
+ struct mipi_dbi *tmp;
+
+ switch (opt) {
+ case 'w':
+ write = true;
+ break;
+ case 'l':
+ list_for_each_entry(tmp, &mipi_dbi_list, list)
+ printf("%s\n", mipi_dbi_name(tmp));
+ return 0;
+ case 'd':
+ dbi = NULL;
+ list_for_each_entry(tmp, &mipi_dbi_list, list) {
+ if (!strcmp(optarg, mipi_dbi_name(tmp))) {
+ dbi = tmp;
+ break;
+ }
+ }
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (!dbi)
+ return -ENODEV;
+
+ if (optind == argc) {
+ for (cmd = 0; cmd < 255; cmd++)
+ mipi_dbi_command_show(dbi, cmd);
+ return 0;
+ }
+
+ ret = kstrtou8(argv[optind++], 16, &cmd);
+ if (ret < 0)
+ return ret;
+
+ if (optind == argc && !write)
+ return mipi_dbi_command_show(dbi, cmd);
+
+ for (i = optind; i < argc; i++) {
+ ret = kstrtou8(argv[optind + i], 16, &val[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return mipi_dbi_command_buf(dbi, cmd, val, argc - optind);
+}
+
+BAREBOX_CMD_HELP_START(mipi_dbi)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-l\t", "list all MIPI DBI devices")
+BAREBOX_CMD_HELP_OPT ("-d DEVICE", "select specific device (default is first registered)")
+BAREBOX_CMD_HELP_OPT ("-w", "issue write command")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(mipi_dbi)
+ .cmd = do_mipi_dbi,
+ BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
+ BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
+BAREBOX_CMD_END
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1b8672fdea..70d1d80953 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -129,6 +129,9 @@ config DRIVER_VIDEO_EDID
This enabled support for reading and parsing EDID data from an attached
monitor.
+config DRIVER_VIDEO_MIPI_DBI
+ bool
+
config DRIVER_VIDEO_BACKLIGHT
bool "Add backlight support"
help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 7f44292789..a7b70d8207 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o
obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
+obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
new file mode 100644
index 0000000000..48b1110f72
--- /dev/null
+++ b/drivers/video/mipi_dbi.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPI Display Bus Interface (DBI) LCD controller support
+ *
+ * Copyright 2016 Noralf Trønnes
+ */
+
+#define pr_fmt(fmt) "mipi-dbi: " fmt
+
+#include <common.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <gpiod.h>
+#include <regulator.h>
+#include <spi/spi.h>
+#include <video/mipi_dbi.h>
+
+#include <video/vpl.h>
+#include <video/mipi_display.h>
+#include <video/fourcc.h>
+
+#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
+
+#define DCS_POWER_MODE_DISPLAY BIT(2)
+#define DCS_POWER_MODE_DISPLAY_NORMAL_MODE BIT(3)
+#define DCS_POWER_MODE_SLEEP_MODE BIT(4)
+#define DCS_POWER_MODE_PARTIAL_MODE BIT(5)
+#define DCS_POWER_MODE_IDLE_MODE BIT(6)
+#define DCS_POWER_MODE_RESERVED_MASK (BIT(0) | BIT(1) | BIT(7))
+
+LIST_HEAD(mipi_dbi_list);
+EXPORT_SYMBOL(mipi_dbi_list);
+
+/**
+ * DOC: overview
+ *
+ * This library provides helpers for MIPI Display Bus Interface (DBI)
+ * compatible display controllers.
+ *
+ * Many controllers for tiny lcd displays are MIPI compliant and can use this
+ * library. If a controller uses registers 0x2A and 0x2B to set the area to
+ * update and uses register 0x2C to write to frame memory, it is most likely
+ * MIPI compliant.
+ *
+ * Only MIPI Type 1 displays are supported since a full frame memory is needed.
+ *
+ * There are 3 MIPI DBI implementation types:
+ *
+ * A. Motorola 6800 type parallel bus
+ *
+ * B. Intel 8080 type parallel bus
+ *
+ * C. SPI type with 3 options:
+ *
+ * 1. 9-bit with the Data/Command signal as the ninth bit
+ * 2. Same as above except it's sent as 16 bits
+ * 3. 8-bit with the Data/Command signal as a separate D/CX pin
+ *
+ * Currently barebox mipi_dbi only supports Type C option 3 with
+ * mipi_dbi_spi_init().
+ */
+
+#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
+({ \
+ if (!len) \
+ pr_debug("cmd=%02x\n", cmd); \
+ else if (len <= 32) \
+ pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
+ else \
+ pr_debug("cmd=%02x, len=%zu\n", cmd, len); \
+})
+
+static const u8 mipi_dbi_dcs_read_commands[] = {
+ MIPI_DCS_GET_DISPLAY_ID,
+ MIPI_DCS_GET_RED_CHANNEL,
+ MIPI_DCS_GET_GREEN_CHANNEL,
+ MIPI_DCS_GET_BLUE_CHANNEL,
+ MIPI_DCS_GET_DISPLAY_STATUS,
+ MIPI_DCS_GET_POWER_MODE,
+ MIPI_DCS_GET_ADDRESS_MODE,
+ MIPI_DCS_GET_PIXEL_FORMAT,
+ MIPI_DCS_GET_DISPLAY_MODE,
+ MIPI_DCS_GET_SIGNAL_MODE,
+ MIPI_DCS_GET_DIAGNOSTIC_RESULT,
+ MIPI_DCS_READ_MEMORY_START,
+ MIPI_DCS_READ_MEMORY_CONTINUE,
+ MIPI_DCS_GET_SCANLINE,
+ MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
+ MIPI_DCS_GET_CONTROL_DISPLAY,
+ MIPI_DCS_GET_POWER_SAVE,
+ MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
+ MIPI_DCS_READ_DDB_START,
+ MIPI_DCS_READ_DDB_CONTINUE,
+ 0, /* sentinel */
+};
+
+bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
+{
+ unsigned int i;
+
+ if (!dbi->read_commands)
+ return false;
+
+ for (i = 0; i < 0xff; i++) {
+ if (!dbi->read_commands[i])
+ return false;
+ if (cmd == dbi->read_commands[i])
+ return true;
+ }
+
+ return false;
+}
+
+int mipi_dbi_command_read_len(int cmd)
+{
+ switch (cmd) {
+ case MIPI_DCS_READ_MEMORY_START:
+ case MIPI_DCS_READ_MEMORY_CONTINUE:
+ return 2;
+ case MIPI_DCS_GET_DISPLAY_ID:
+ return 3;
+ case MIPI_DCS_GET_DISPLAY_STATUS:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+/**
+ * mipi_dbi_command_read - MIPI DCS read command
+ * @dbi: MIPI DBI structure
+ * @cmd: Command
+ * @val: Value read
+ *
+ * Send MIPI DCS read command to the controller.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
+{
+ if (!dbi->read_commands)
+ return -EACCES;
+
+ if (!mipi_dbi_command_is_read(dbi, cmd))
+ return -EINVAL;
+
+ return mipi_dbi_command_buf(dbi, cmd, val, 1);
+}
+EXPORT_SYMBOL(mipi_dbi_command_read);
+
+/**
+ * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
+ * @dbi: MIPI DBI structure
+ * @cmd: Command
+ * @data: Parameter buffer
+ * @len: Buffer length
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
+{
+ u8 *cmdbuf;
+ int ret;
+
+ /* SPI requires dma-safe buffers */
+ cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
+ if (!cmdbuf)
+ return -ENOMEM;
+
+ ret = dbi->command(dbi, cmdbuf, data, len);
+
+ kfree(cmdbuf);
+
+ return ret;
+}
+EXPORT_SYMBOL(mipi_dbi_command_buf);
+
+/* This should only be used by mipi_dbi_command() */
+int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
+ size_t len)
+{
+ u8 *buf;
+ int ret;
+
+ buf = kmemdup(data, len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
+
+ kfree(buf);
+
+ return ret;
+}
+EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
+
+/**
+ * mipi_dbi_hw_reset - Hardware reset of controller
+ * @dbi: MIPI DBI structure
+ *
+ * Reset controller if the &mipi_dbi->reset gpio is set.
+ */
+void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
+{
+ if (!gpio_is_valid(dbi->reset))
+ return;
+
+ gpiod_set_value(dbi->reset, 0);
+ udelay(20);
+ gpiod_set_value(dbi->reset, 1);
+ mdelay(120);
+}
+EXPORT_SYMBOL(mipi_dbi_hw_reset);
+
+/**
+ * mipi_dbi_display_is_on - Check if display is on
+ * @dbi: MIPI DBI structure
+ *
+ * This function checks the Power Mode register (if readable) to see if
+ * display output is turned on. This can be used to see if the bootloader
+ * has already turned on the display avoiding flicker when the pipeline is
+ * enabled.
+ *
+ * Returns:
+ * true if the display can be verified to be on, false otherwise.
+ */
+bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
+{
+ u8 val;
+
+ if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
+ return false;
+
+ val &= ~DCS_POWER_MODE_RESERVED_MASK;
+
+ /* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
+ if (val != (DCS_POWER_MODE_DISPLAY |
+ DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
+ return false;
+
+ pr_debug("Display is ON\n");
+
+ return true;
+}
+EXPORT_SYMBOL(mipi_dbi_display_is_on);
+
+#if IS_ENABLED(CONFIG_SPI)
+
+/**
+ * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
+ * @spi: SPI device
+ * @len: The transfer buffer length.
+ *
+ * Many controllers have a max speed of 10MHz, but can be pushed way beyond
+ * that. Increase reliability by running pixel data at max speed and the rest
+ * at 10MHz, preventing transfer glitches from messing up the init settings.
+ */
+u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
+{
+ if (len > 64)
+ return 0; /* use default */
+
+ return min_t(u32, 10000000, spi->max_speed_hz);
+}
+EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
+
+static bool mipi_dbi_machine_little_endian(void)
+{
+#if defined(__LITTLE_ENDIAN)
+ return true;
+#else
+ return false;
+#endif
+}
+
+/* MIPI DBI Type C Option 3 */
+
+static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
+ u8 *data, size_t len)
+{
+ struct spi_device *spi = dbi->spi;
+ u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
+ spi->max_speed_hz / 2);
+ struct spi_transfer tr[2] = {
+ {
+ .speed_hz = speed_hz,
+ .tx_buf = cmd,
+ .len = 1,
+ }, {
+ .speed_hz = speed_hz,
+ .len = len,
+ },
+ };
+ struct spi_message m;
+ u8 *buf;
+ int ret;
+
+ if (!len)
+ return -EINVAL;
+
+ /*
+ * Support non-standard 24-bit and 32-bit Nokia read commands which
+ * start with a dummy clock, so we need to read an extra byte.
+ */
+ if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
+ *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
+ if (!(len == 3 || len == 4))
+ return -EINVAL;
+
+ tr[1].len = len + 1;
+ }
+
+ buf = kmalloc(tr[1].len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tr[1].rx_buf = buf;
+ gpiod_set_value(dbi->dc, 0);
+
+ spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
+ ret = spi_sync(spi, &m);
+ if (ret)
+ goto err_free;
+
+ if (tr[1].len == len) {
+ memcpy(data, buf, len);
+ } else {
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
+ }
+
+ MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
+
+err_free:
+ kfree(buf);
+
+ return ret;
+}
+
+static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
+ u8 *par, size_t num)
+{
+ struct spi_device *spi = dbi->spi;
+ unsigned int bpw = 8;
+ u32 speed_hz;
+ int ret;
+
+ if (mipi_dbi_command_is_read(dbi, *cmd))
+ return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
+
+ MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
+
+ gpiod_set_value(dbi->dc, 0);
+ speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
+ ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
+ if (ret || !num)
+ return ret;
+
+ if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
+ bpw = 16;
+
+ gpiod_set_value(dbi->dc, 1);
+ speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
+
+ return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
+}
+
+/**
+ * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
+ * @spi: SPI device
+ * @dbi: MIPI DBI structure to initialize
+ * @dc: D/C gpio
+ *
+ * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
+ * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
+ * a driver-specific init.
+ *
+ * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported,
+ * because barebox has no generic way yet to require a 9-bit SPI transfer
+ *
+ * If the SPI master driver doesn't support the necessary bits per word,
+ * the following transformation is used:
+ *
+ * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
+ * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
+ int dc)
+{
+ struct device_d *dev = &spi->dev;
+
+ dbi->spi = spi;
+ dbi->read_commands = mipi_dbi_dcs_read_commands;
+
+ if (!gpio_is_valid(dc)) {
+ dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
+ return -ENOSYS;
+ }
+
+ dbi->command = mipi_dbi_typec3_command;
+ dbi->dc = dc;
+ // TODO: can we just force 16 bit?
+ if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
+ dbi->swap_bytes = true;
+
+ dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
+
+ list_add(&dbi->list, &mipi_dbi_list);
+ return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_spi_init);
+
+/**
+ * mipi_dbi_spi_transfer - SPI transfer helper
+ * @spi: SPI device
+ * @speed_hz: Override speed (optional)
+ * @bpw: Bits per word
+ * @buf: Buffer to transfer
+ * @len: Buffer length
+ *
+ * This SPI transfer helper breaks up the transfer of @buf into chunks which
+ * the SPI controller driver can handle.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
+ u8 bpw, const void *buf, size_t len)
+{
+ size_t max_chunk = spi_max_transfer_size(spi);
+ struct spi_transfer tr = {
+ .bits_per_word = bpw,
+ .speed_hz = speed_hz,
+ };
+ struct spi_message m;
+ size_t chunk;
+ int ret;
+
+ spi_message_init_with_transfers(&m, &tr, 1);
+
+ while (len) {
+ chunk = min(len, max_chunk);
+
+ tr.tx_buf = buf;
+ tr.len = chunk;
+ buf += chunk;
+ len -= chunk;
+
+ ret = spi_sync(spi, &m);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_spi_transfer);
+
+#endif /* CONFIG_SPI */
+
+MODULE_LICENSE("GPL");
diff --git a/include/spi/spi.h b/include/spi/spi.h
index c5ad6bd39f..d133e0e212 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -409,6 +409,26 @@ spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
list_add_tail(&t->transfer_list, &m->transfers);
}
+/**
+ * spi_message_init_with_transfers - Initialize spi_message and append transfers
+ * @m: spi_message to be initialized
+ * @xfers: An array of spi transfers
+ * @num_xfers: Number of items in the xfer array
+ *
+ * This function initializes the given spi_message and adds each spi_transfer in
+ * the given array to the message.
+ */
+static inline void
+spi_message_init_with_transfers(struct spi_message *m,
+struct spi_transfer *xfers, unsigned int num_xfers)
+{
+ unsigned int i;
+
+ spi_message_init(m);
+ for (i = 0; i < num_xfers; ++i)
+ spi_message_add_tail(&xfers[i], m);
+}
+
static inline void
spi_transfer_del(struct spi_transfer *t)
{
diff --git a/include/video/mipi_dbi.h b/include/video/mipi_dbi.h
new file mode 100644
index 0000000000..92fdc500d1
--- /dev/null
+++ b/include/video/mipi_dbi.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * MIPI Display Bus Interface (DBI) LCD controller support
+ *
+ * Copyright 2016 Noralf Trønnes
+ */
+
+#ifndef __LINUX_MIPI_DBI_H
+#define __LINUX_MIPI_DBI_H
+
+#include <linux/types.h>
+#include <spi/spi.h>
+#include <driver.h>
+
+struct regulator;
+struct fb_videomode;
+
+/**
+ * struct mipi_dbi - MIPI DBI interface
+ */
+struct mipi_dbi {
+ /**
+ * @command: Bus specific callback executing commands.
+ */
+ int (*command)(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num);
+
+ /**
+ * @read_commands: Array of read commands terminated by a zero entry.
+ * Reading is disabled if this is NULL.
+ */
+ const u8 *read_commands;
+
+ /**
+ * @swap_bytes: Swap bytes in buffer before transfer
+ */
+ bool swap_bytes;
+
+ /**
+ * @reset: Optional reset gpio
+ */
+ int reset;
+
+ /* Type C specific */
+
+ /**
+ * @spi: SPI device
+ */
+ struct spi_device *spi;
+
+ /**
+ * @dc: Optional D/C gpio.
+ */
+ int dc;
+
+ struct list_head list;
+};
+
+static inline const char *mipi_dbi_name(struct mipi_dbi *dbi)
+{
+ return dev_name(&dbi->spi->dev);
+}
+
+int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
+ int dc);
+void mipi_dbi_hw_reset(struct mipi_dbi *dbi);
+bool mipi_dbi_display_is_on(struct mipi_dbi *dbi);
+
+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,
+ u8 bpw, const void *buf, size_t len);
+
+int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val);
+int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len);
+int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
+ size_t len);
+
+/**
+ * mipi_dbi_command - MIPI DCS command with optional parameter(s)
+ * @dbi: MIPI DBI structure
+ * @cmd: Command
+ * @seq: Optional parameter(s)
+ *
+ * Send MIPI DCS command to the controller. Use mipi_dbi_command_read() for
+ * get/read.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+#define mipi_dbi_command(dbi, cmd, seq...) \
+({ \
+ const u8 d[] = { seq }; \
+ struct device_d *dev = &(dbi)->spi->dev; \
+ int ret; \
+ ret = mipi_dbi_command_stackbuf(dbi, cmd, d, ARRAY_SIZE(d)); \
+ if (ret) \
+ dev_err(dev, "error %pe when sending command %#02x\n", ERR_PTR(ret), cmd); \
+ ret; \
+})
+
+bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd);
+int mipi_dbi_command_read_len(int cmd);
+
+extern struct list_head mipi_dbi_list;
+
+#endif /* __LINUX_MIPI_DBI_H */
diff --git a/include/video/mipi_display.h b/include/video/mipi_display.h
new file mode 100644
index 0000000000..b6d8b87423
--- /dev/null
+++ b/include/video/mipi_display.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Defines for Mobile Industry Processor Interface (MIPI(R))
+ * Display Working Group standards: DSI, DCS, DBI, DPI
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ */
+#ifndef MIPI_DISPLAY_H
+#define MIPI_DISPLAY_H
+
+/* MIPI DSI Processor-to-Peripheral transaction types */
+enum {
+ MIPI_DSI_V_SYNC_START = 0x01,
+ MIPI_DSI_V_SYNC_END = 0x11,
+ MIPI_DSI_H_SYNC_START = 0x21,
+ MIPI_DSI_H_SYNC_END = 0x31,
+
+ MIPI_DSI_COMPRESSION_MODE = 0x07,
+ MIPI_DSI_END_OF_TRANSMISSION = 0x08,
+
+ MIPI_DSI_COLOR_MODE_OFF = 0x02,
+ MIPI_DSI_COLOR_MODE_ON = 0x12,
+ MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22,
+ MIPI_DSI_TURN_ON_PERIPHERAL = 0x32,
+
+ MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03,
+ MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13,
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23,
+
+ MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04,
+ MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14,
+ MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24,
+
+ MIPI_DSI_DCS_SHORT_WRITE = 0x05,
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15,
+
+ MIPI_DSI_DCS_READ = 0x06,
+ MIPI_DSI_EXECUTE_QUEUE = 0x16,
+
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37,
+
+ MIPI_DSI_NULL_PACKET = 0x09,
+ MIPI_DSI_BLANKING_PACKET = 0x19,
+ MIPI_DSI_GENERIC_LONG_WRITE = 0x29,
+ MIPI_DSI_DCS_LONG_WRITE = 0x39,
+
+ MIPI_DSI_PICTURE_PARAMETER_SET = 0x0a,
+ MIPI_DSI_COMPRESSED_PIXEL_STREAM = 0x0b,
+
+ MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e,
+ MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e,
+};
+
+/* MIPI DSI Peripheral-to-Processor transaction types */
+enum {
+ MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02,
+ MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12,
+ MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a,
+ MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22,
+};
+
+/* MIPI DCS commands */
+enum {
+ MIPI_DCS_NOP = 0x00,
+ MIPI_DCS_SOFT_RESET = 0x01,
+ MIPI_DCS_GET_COMPRESSION_MODE = 0x03,
+ MIPI_DCS_GET_DISPLAY_ID = 0x04,
+ MIPI_DCS_GET_ERROR_COUNT_ON_DSI = 0x05,
+ MIPI_DCS_GET_RED_CHANNEL = 0x06,
+ MIPI_DCS_GET_GREEN_CHANNEL = 0x07,
+ MIPI_DCS_GET_BLUE_CHANNEL = 0x08,
+ MIPI_DCS_GET_DISPLAY_STATUS = 0x09,
+ MIPI_DCS_GET_POWER_MODE = 0x0A,
+ MIPI_DCS_GET_ADDRESS_MODE = 0x0B,
+ MIPI_DCS_GET_PIXEL_FORMAT = 0x0C,
+ MIPI_DCS_GET_DISPLAY_MODE = 0x0D,
+ MIPI_DCS_GET_SIGNAL_MODE = 0x0E,
+ MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F,
+ MIPI_DCS_ENTER_SLEEP_MODE = 0x10,
+ MIPI_DCS_EXIT_SLEEP_MODE = 0x11,
+ MIPI_DCS_ENTER_PARTIAL_MODE = 0x12,
+ MIPI_DCS_ENTER_NORMAL_MODE = 0x13,
+ MIPI_DCS_GET_IMAGE_CHECKSUM_RGB = 0x14,
+ MIPI_DCS_GET_IMAGE_CHECKSUM_CT = 0x15,
+ MIPI_DCS_EXIT_INVERT_MODE = 0x20,
+ MIPI_DCS_ENTER_INVERT_MODE = 0x21,
+ MIPI_DCS_SET_GAMMA_CURVE = 0x26,
+ MIPI_DCS_SET_DISPLAY_OFF = 0x28,
+ MIPI_DCS_SET_DISPLAY_ON = 0x29,
+ MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A,
+ MIPI_DCS_SET_PAGE_ADDRESS = 0x2B,
+ MIPI_DCS_WRITE_MEMORY_START = 0x2C,
+ MIPI_DCS_WRITE_LUT = 0x2D,
+ MIPI_DCS_READ_MEMORY_START = 0x2E,
+ MIPI_DCS_SET_PARTIAL_ROWS = 0x30, /* MIPI DCS 1.02 - MIPI_DCS_SET_PARTIAL_AREA before that */
+ MIPI_DCS_SET_PARTIAL_COLUMNS = 0x31,
+ MIPI_DCS_SET_SCROLL_AREA = 0x33,
+ MIPI_DCS_SET_TEAR_OFF = 0x34,
+ MIPI_DCS_SET_TEAR_ON = 0x35,
+ MIPI_DCS_SET_ADDRESS_MODE = 0x36,
+ MIPI_DCS_SET_SCROLL_START = 0x37,
+ MIPI_DCS_EXIT_IDLE_MODE = 0x38,
+ MIPI_DCS_ENTER_IDLE_MODE = 0x39,
+ MIPI_DCS_SET_PIXEL_FORMAT = 0x3A,
+ MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C,
+ MIPI_DCS_SET_3D_CONTROL = 0x3D,
+ MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E,
+ MIPI_DCS_GET_3D_CONTROL = 0x3F,
+ MIPI_DCS_SET_VSYNC_TIMING = 0x40,
+ MIPI_DCS_SET_TEAR_SCANLINE = 0x44,
+ MIPI_DCS_GET_SCANLINE = 0x45,
+ MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51, /* MIPI DCS 1.3 */
+ MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52, /* MIPI DCS 1.3 */
+ MIPI_DCS_WRITE_CONTROL_DISPLAY = 0x53, /* MIPI DCS 1.3 */
+ MIPI_DCS_GET_CONTROL_DISPLAY = 0x54, /* MIPI DCS 1.3 */
+ MIPI_DCS_WRITE_POWER_SAVE = 0x55, /* MIPI DCS 1.3 */
+ MIPI_DCS_GET_POWER_SAVE = 0x56, /* MIPI DCS 1.3 */
+ MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E, /* MIPI DCS 1.3 */
+ MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F, /* MIPI DCS 1.3 */
+ MIPI_DCS_READ_DDB_START = 0xA1,
+ MIPI_DCS_READ_PPS_START = 0xA2,
+ MIPI_DCS_READ_DDB_CONTINUE = 0xA8,
+ MIPI_DCS_READ_PPS_CONTINUE = 0xA9,
+};
+
+/* MIPI DCS pixel formats */
+#define MIPI_DCS_PIXEL_FMT_24BIT 7
+#define MIPI_DCS_PIXEL_FMT_18BIT 6
+#define MIPI_DCS_PIXEL_FMT_16BIT 5
+#define MIPI_DCS_PIXEL_FMT_12BIT 3
+#define MIPI_DCS_PIXEL_FMT_8BIT 2
+#define MIPI_DCS_PIXEL_FMT_3BIT 1
+
+#endif