* [PATCH v2 0/3] drm: panel: Add Generic MIPI Panel Driver
@ 2025-02-26 11:25 Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix Hironori KIKUCHI
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Hironori KIKUCHI @ 2025-02-26 11:25 UTC (permalink / raw)
To: linux-kernel
Cc: Hironori KIKUCHI, Neil Armstrong, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jakub Kicinski, dri-devel, devicetree
This is a Generic MIPI-DSI / DPI(+SPI) dual stack panel driver.
Although the MIPI specifications define how to communicate with a panel
to display an image, some panels still require a panel-specific
initialization sequence to be sent.
While there are many dedicated drivers for such panels, they are very
similar to each other, except for the configuration parameters, including
the initialization sequence for each panel.
Since there are numerous panels in the wild, adding parameters to
the driver for each panel every time is a pain.
Instead, this driver offers more generic and convenient method.
Its fundamental approach is similar to the `panel-mipi-dbi` driver,
which sends an initialization sequence stored in a firmware file.
Moreover, this driver allows display modes, timings, and panel
configuration parameters to be stored in the same file.
I've also come up with a firmware generator [1].
It helps to create or configure the panel parameters on the web.
As a reference, similar generic drivers currently exist:
* drivers/gpu/drm/panel/panel-simple.c
- Supports wide variety of panels, but no init-sequence support
* drivers/gpu/drm/tiny/panel-mipi-dbi.c
- Supports init-sequence, but only supports DBI panels
* drivers/gpu/drm/panel/panel-dsi-cm.c
- Supports DSI Command Mode, but no init-sequence support
[1]: https://kikuchan.github.io/panel-firmware-generator/
v2:
- Drop redundant properties from the DT binding
- Fix the compatible property and examples in the DT binding
- Drop support for the redundant DT properties from the driver
- Fix minor issue in the driver
- Add panels to the DT binding
Regards,
kikuchan.
Hironori KIKUCHI (3):
dt-bindings: vendor-prefixes: Add hothmi vendor prefix
dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels
drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels
.../bindings/display/panel/panel-mipi.yaml | 121 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-mipi.c | 1181 +++++++++++++++++
5 files changed, 1315 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
create mode 100644 drivers/gpu/drm/panel/panel-mipi.c
--
2.48.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix
2025-02-26 11:25 [PATCH v2 0/3] drm: panel: Add Generic MIPI Panel Driver Hironori KIKUCHI
@ 2025-02-26 11:25 ` Hironori KIKUCHI
2025-03-03 13:06 ` Rob Herring (Arm)
2025-02-26 11:25 ` [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels Hironori KIKUCHI
2 siblings, 1 reply; 8+ messages in thread
From: Hironori KIKUCHI @ 2025-02-26 11:25 UTC (permalink / raw)
To: linux-kernel
Cc: Hironori KIKUCHI, Neil Armstrong, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jakub Kicinski, dri-devel, devicetree
Add prefix for Hotdisplay Technology Co.Ltd
Signed-off-by: Hironori KIKUCHI <kikuchan98@gmail.com>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 5079ca6ce1d..44108bd6ae2 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -650,6 +650,8 @@ patternProperties:
description: Shenzhen Hope Microelectronics Co., Ltd.
"^hoperun,.*":
description: Jiangsu HopeRun Software Co., Ltd.
+ "^hothmi,.*":
+ description: Hotdisplay Technology Co.Ltd
"^hp,.*":
description: Hewlett Packard Inc.
"^hpe,.*":
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels
2025-02-26 11:25 [PATCH v2 0/3] drm: panel: Add Generic MIPI Panel Driver Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix Hironori KIKUCHI
@ 2025-02-26 11:25 ` Hironori KIKUCHI
2025-03-03 13:12 ` Rob Herring
2025-02-26 11:25 ` [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels Hironori KIKUCHI
2 siblings, 1 reply; 8+ messages in thread
From: Hironori KIKUCHI @ 2025-02-26 11:25 UTC (permalink / raw)
To: linux-kernel
Cc: Hironori KIKUCHI, Neil Armstrong, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jakub Kicinski, dri-devel, devicetree
This is a binding for generic MIPI-DSI/DPI panels that require
initialization with a simple command sequence before use.
The initialization of the panel requires a firmware binary which can be
made with the Panel Firmware Generator[1] on the web.
Add 4 new panels, as they are available on the same page[1] as a preset
(excluding already included ones).
Note that the "xx" in the panel name is taken from a product's name,
not a wildcard.
[1]: https://kikuchan.github.io/panel-firmware-generator/
Signed-off-by: Hironori KIKUCHI <kikuchan98@gmail.com>
---
.../bindings/display/panel/panel-mipi.yaml | 121 ++++++++++++++++++
1 file changed, 121 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
diff --git a/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml b/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
new file mode 100644
index 00000000000..d70cf0063fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/panel-mipi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic MIPI-DSI/DPI Panels Requiring Initialization
+
+maintainers:
+ - Hironori KIKUCHI <kikuchan98@gmail.com>
+
+description: This is a binding for generic MIPI-DSI/DPI panels that require
+ initialization with a simple command sequence before use.
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ # Unknown 3.35" 720x480 24-bit IPS LCD panel
+ # used in Anbernic RG 34XX
+ - anbernic,rg34xx-panel
+ # Unknown 4.00" 640x480 24-bit IPS LCD panel (YLM-LBV0400001X-V1)
+ # used in Anbernic RG40XX series
+ - anbernic,rg40xx-panel
+ # Unknown 3.95" 720x720 24-bit IPS LCD panel (YLM-LBN0395004H-V1)
+ # used in Anbernic RG CubeXX
+ - anbernic,rgcubexx-panel
+ - const: panel-mipi-dpi-spi
+ - items:
+ - enum:
+ # HOTHMI TFT-H028B23VGIST7G40-V1 2.80" 480x640 TFT LCD panel
+ - hothmi,tft-h028b23vgist7g40-v1
+ - const: panel-mipi-dsi
+
+ reg:
+ description: DSI / SPI channel used by that screen
+ maxItems: 1
+
+ power-supply: true
+
+ io-supply:
+ description: I/O system regulator.
+ No need to set if this is the same as polwer-supply.
+
+ dc-gpios:
+ maxItems: 1
+ description: Controller data/command selection (D/CX) in 4-line SPI mode.
+ If not set, the controller is in 3-line SPI mode.
+ Disallowed for DSI.
+
+ port: true
+ reset-gpios: true
+
+ backlight: true
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: panel-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - panel-mipi-dpi-spi
+ then:
+ # SPI mode
+ $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - panel-mipi-dsi
+ then:
+ # DSI mode
+ properties:
+ dc-gpios: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "hothmi,tft-h028b23vgist7g40-v1", "panel-mipi-dsi";
+ reg = <0>;
+
+ port {
+ mipi_in_panel: endpoint {
+ remote-endpoint = <&mipi_out_panel>;
+ };
+ };
+ };
+ };
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "anbernic,rg40xx-panel", "panel-mipi-dpi-spi";
+ reg = <0>;
+
+ spi-max-frequency = <40000000>;
+
+ dc-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
+
+ backlight = <&backlight>;
+ };
+ };
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels
2025-02-26 11:25 [PATCH v2 0/3] drm: panel: Add Generic MIPI Panel Driver Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels Hironori KIKUCHI
@ 2025-02-26 11:25 ` Hironori KIKUCHI
2025-03-03 9:26 ` Krzysztof Kozlowski
2025-03-06 7:53 ` kernel test robot
2 siblings, 2 replies; 8+ messages in thread
From: Hironori KIKUCHI @ 2025-02-26 11:25 UTC (permalink / raw)
To: linux-kernel
Cc: Hironori KIKUCHI, Neil Armstrong, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jakub Kicinski, dri-devel, devicetree
Although the MIPI specifications define how to communicate with a panel
to display an image, some panels still require a panel-specific
initialization sequence to be sent.
This is a driver for such generic MIPI-DSI/DPI panels that require
initialization with a simple command sequence before use.
Its fundamental approach is similar to `panel-mipi-dbi` driver,
which sends an initialization sequence stored in a firmware file.
Moreover, this driver allows display modes, timings, and panel
configuration parameters to be stored in the same file.
Signed-off-by: Hironori KIKUCHI <kikuchan98@gmail.com>
---
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-mipi.c | 1181 ++++++++++++++++++++++++++++
3 files changed, 1192 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-mipi.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d7469c565d1..46eea1974a0 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -408,6 +408,16 @@ config DRM_PANEL_MANTIX_MLAF057WE51
has a resolution of 720x1440 pixels, a built in backlight and touch
controller.
+config DRM_PANEL_MIPI
+ tristate "Generic MIPI-DSI/DPI(+SPI) panel"
+ depends on OF
+ depends on SPI || DRM_MIPI_DSI
+ select DRM_MIPI_DBI if SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for Generic MIPI-DSI /
+ MIPI-DPI(+SPI) panels.
+
config DRM_PANEL_NEC_NL8048HL11
tristate "NEC NL8048HL11 RGB panel"
depends on GPIOLIB && OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 7dcf72646ca..22276255a7b 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
+obj-$(CONFIG_DRM_PANEL_MIPI) += panel-mipi.o
obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3052C) += panel-newvision-nv3052c.o
diff --git a/drivers/gpu/drm/panel/panel-mipi.c b/drivers/gpu/drm/panel/panel-mipi.c
new file mode 100644
index 00000000000..a4da1fd2e1e
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-mipi.c
@@ -0,0 +1,1181 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic MIPI-DSI/DPI(+SPI) Panel Driver
+ *
+ * Supported panels:
+ * - A generic MIPI-DSI panel which implements basic DCS
+ * - A generic MIPI-DPI panel which implements basic DCS over SPI
+ *
+ * Copyright (C) 2025, Hironori KIKUCHI <kikuchan98@gmail.com>
+ */
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <linux/backlight.h>
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+/*
+ * The display panel configuration can be stored in a firmware file,
+ * under the firmware directory of the system.
+ *
+ * The name of the file is `panels/<compatible>.panel`, where the
+ * `<compatible>` is the first string of the `compatible` property
+ * defined in the Device Tree for the device.
+ *
+ * File Layout:
+ * A binary file composed with the following data:
+ * <header>
+ * <config>
+ * <timings>
+ * <init-sequence>
+ *
+ * The 'header':
+ * A `struct panel_firmware_header`.
+ * The `file_format_version` must be `1`.
+ *
+ * The 'config':
+ * A `struct panel_firmware_config`.
+ * The values are in big-endian.
+ *
+ * The 'timings':
+ * An array of `struct panel_firmware_panel_timing`.
+ * Its length is `num_timings` in the config.
+ * The values are in big-endian.
+ *
+ * The 'init-sequence':
+ * MIPI commands to execute when the display pipeline is enabled.
+ * This is used to configure the display controller, and it may
+ * contains panel specific parameters as well.
+ *
+ * The commands are encoded and stored in a byte array:
+ *
+ * 0x00 : sleep 10 ms
+ * 0x01 <M> : MIPI command <M> with no arguments
+ * 0x02 <M> <a> : MIPI command <M> with 1 argument <a>
+ * 0x03 <M> <a> <b> : MIPI command <M> with 2 arguments <a> <b>
+ * :
+ * 0x7f <M> <...> : MIPI command <M> with 126 arguments <...>
+ * 0x80 : sleep 100 ms
+ * 0x81 - 0xff : reserved
+ *
+ * Example:
+ * command 0x11
+ * sleep 10ms
+ * command 0xb1 arguments 0x01 0x2c 0x2d
+ * command 0x29
+ * sleep 130ms
+ *
+ * Byte sequence:
+ * 0x01 0x11
+ * 0x00
+ * 0x04 0xb1 0x01 0x2c 0x2d
+ * 0x01 0x29
+ * 0x80 0x00 0x00 0x00
+ *
+ */
+static const u8 panel_firmware_magic[15] = {
+ 0x50, 0x41, 0x4e, 0x45, 0x4c, 0x2d, 0x46, 0x49,
+ 0x52, 0x4d, 0x57, 0x41, 0x52, 0x45, 0x00,
+};
+
+struct panel_firmware_header {
+ u8 magic[15];
+ u8 file_format_version; /* must be 1 */
+} __packed;
+
+struct panel_firmware_config {
+ u16 width_mm, height_mm;
+ u16 rotation;
+ u8 _reserved_1[2];
+ u8 _reserved_2[8];
+
+ u16 reset_delay; /* delay after the reset command, in ms */
+ u16 init_delay; /* delay for sending the initial command sequence, in ms */
+ u16 sleep_delay; /* delay after the sleep command, in ms */
+ u16 backlight_delay; /* delay for enabling the backlight, in ms */
+ u16 _reserved_3[4];
+
+ u16 dsi_lanes; /* unsigned int */
+ u16 dsi_format; /* enum mipi_dsi_pixel_format */
+ u32 dsi_mode_flags; /* unsigned long */
+ u32 bus_flags; /* struct drm_bus_flags */
+ u8 _reserved_4[2];
+ u8 preferred_timing;
+ u8 num_timings;
+} __packed;
+
+struct panel_firmware_panel_timing {
+ u16 hactive;
+ u16 hfp;
+ u16 hslen;
+ u16 hbp;
+
+ u16 vactive;
+ u16 vfp;
+ u16 vslen;
+ u16 vbp;
+
+ u32 dclk; /* in kHz */
+ u32 flags; /* include/drm/drm_modes.h DRM_MODE_FLAG_* */
+
+ u8 _reserved_1[8];
+} __packed;
+
+struct panel_commands {
+ const u8 *data;
+ size_t size;
+};
+
+struct panel_firmware {
+ const struct firmware *blob;
+ const struct panel_firmware_config *config;
+ const struct panel_firmware_panel_timing *timings;
+ struct panel_commands commands;
+};
+
+struct panel_mipi {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct mipi_dbi dbi;
+
+ struct mutex lock;
+
+ struct regulator_bulk_data supplies[2];
+ struct gpio_desc *reset;
+
+ unsigned int reset_delay;
+ unsigned int init_delay;
+ unsigned int sleep_delay;
+ unsigned int backlight_delay;
+
+ unsigned int dsi_lanes;
+ enum mipi_dsi_pixel_format dsi_format;
+
+ /* Panel info */
+ u32 width_mm, height_mm;
+ u32 bus_format, bus_flags;
+ enum drm_panel_orientation orientation;
+
+ struct list_head mode_list;
+
+ struct panel_firmware *firmware;
+ struct panel_commands commands;
+
+ int (*write_command)(struct panel_mipi *mipi, const u8 *buf,
+ size_t len);
+ int (*read_command)(struct panel_mipi *mipi, u8 cmd, u8 *buf,
+ size_t len);
+
+ /* debugfs */
+ struct dentry *debugfs;
+ u8 recvbuf[1024];
+ size_t recvlen;
+ int display_id;
+};
+
+static inline struct panel_mipi *to_panel_mipi(struct drm_panel *panel)
+{
+ return container_of(panel, struct panel_mipi, panel);
+}
+
+static int panel_mipi_dsi_write(struct panel_mipi *mipi, const u8 *buf,
+ size_t len)
+{
+ int ret;
+
+ ret = mipi_dsi_dcs_write_buffer(mipi->dsi, buf, len);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int panel_mipi_dbi_write(struct panel_mipi *mipi, const u8 *buf,
+ size_t len)
+{
+ return mipi_dbi_command_stackbuf(&mipi->dbi, buf[0], buf + 1, len - 1);
+}
+
+#define panel_mipi_write(mipi, ...) \
+ ({ \
+ u8 _buf[] = { __VA_ARGS__ }; \
+ mipi->write_command(mipi, _buf, ARRAY_SIZE(_buf)); \
+ })
+
+static int panel_mipi_dsi_read(struct panel_mipi *mipi, u8 cmd, u8 *buf,
+ size_t len)
+{
+ int ret;
+
+ ret = mipi_dsi_set_maximum_return_packet_size(mipi->dsi, len);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_dcs_read(mipi->dsi, cmd, buf, len);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int panel_mipi_dbi_read(struct panel_mipi *mipi, u8 cmd, u8 *buf,
+ size_t len)
+{
+ void *recvbuf;
+ int err;
+
+ /* alloc DMA-safe buffer */
+ recvbuf = kzalloc(len, GFP_KERNEL);
+ if (!recvbuf)
+ return -ENOMEM;
+
+ err = mipi_dbi_command_buf(&mipi->dbi, cmd, recvbuf, len);
+ if (!err)
+ memcpy(buf, recvbuf, len);
+
+ kfree(recvbuf);
+
+ return err;
+}
+
+#define panel_mipi_read(mipi, cmd, buf, len) \
+ (mipi)->read_command((mipi), (cmd), (buf), (len))
+
+static unsigned long panel_mipi_dsi_set_mode_flags(struct panel_mipi *mipi,
+ unsigned long new_flags)
+{
+ unsigned long old_flags;
+
+ if (!mipi->dsi)
+ return 0;
+
+ old_flags = mipi->dsi->mode_flags;
+
+ mipi->dsi->mode_flags = new_flags;
+
+ return old_flags;
+}
+
+static bool panel_mipi_dsi_set_lpm(struct panel_mipi *mipi, bool set)
+{
+ unsigned long old_flags;
+
+ if (!mipi->dsi)
+ return false;
+
+ old_flags = panel_mipi_dsi_set_mode_flags(
+ mipi, set ? mipi->dsi->mode_flags | MIPI_DSI_MODE_LPM :
+ mipi->dsi->mode_flags & ~MIPI_DSI_MODE_LPM);
+
+ return old_flags & MIPI_DSI_MODE_LPM ? true : false;
+}
+
+#define PANEL_MIPI_TRANSACTION(mipi, stmt) \
+ do { \
+ mutex_lock(&(mipi)->lock); \
+ do { \
+ stmt; \
+ } while (0); \
+ mutex_unlock(&(mipi)->lock); \
+ } while (0)
+
+#define PANEL_MIPI_TRANSACTION_LPM(mipi, lpm, stmt) \
+ PANEL_MIPI_TRANSACTION( \
+ mipi, \
+ bool _lpm_backup = panel_mipi_dsi_set_lpm((mipi), (lpm)); \
+ do { stmt; } while (0); \
+ panel_mipi_dsi_set_lpm((mipi), _lpm_backup))
+
+static int panel_mipi_validate_commands(struct panel_mipi *mipi, const u8 *data,
+ size_t size, size_t *valid_len)
+{
+ size_t i = 0;
+
+ /* sanity check */
+ while (i < size) {
+ u8 inst = data[i];
+ bool ext = (inst & 0x80) ? true : false;
+ u8 len = inst & 0x7f;
+
+ if (ext && len > 0)
+ return -EINVAL; /* reserved */
+
+ if (i + 1 + len > size)
+ break;
+
+ i += 1 + len;
+ }
+
+ *valid_len = i;
+
+ return 0;
+}
+
+static int panel_mipi_load_commands(struct panel_mipi *mipi, const u8 *data,
+ size_t size)
+{
+ int err;
+ size_t valid_len;
+
+ err = panel_mipi_validate_commands(mipi, data, size, &valid_len);
+ if (err)
+ return err;
+
+ if (valid_len != size)
+ return -EINVAL;
+
+ mipi->commands.data = data;
+ mipi->commands.size = size;
+
+ return 0;
+}
+
+static int panel_mipi_write_commands(struct panel_mipi *mipi, const u8 *data,
+ size_t size)
+{
+ const u8 *read_commands;
+ unsigned int write_memory_bpw;
+ size_t i = 0;
+ int err = 0;
+
+ if (!data)
+ return 0;
+
+ /*
+ * Disable interpretation for special commands for DBI
+ *
+ * This is required because the vendor specific custom commands
+ * may change its command set to the other.
+ */
+ read_commands = mipi->dbi.read_commands;
+ write_memory_bpw = mipi->dbi.write_memory_bpw;
+ mipi->dbi.read_commands = NULL;
+ mipi->dbi.write_memory_bpw = 8;
+
+ while (i < size) {
+ u8 inst = data[i++];
+ bool ext = (inst & 0x80) ? true : false;
+ u8 len = inst & 0x7f;
+
+ if (len == 0x00) {
+ msleep(ext ? 100 : 10);
+ continue;
+ }
+
+ err = mipi->write_command(mipi, data + i, len);
+ if (err)
+ break;
+
+ i += len;
+ }
+
+ /* restore */
+ mipi->dbi.read_commands = read_commands;
+ mipi->dbi.write_memory_bpw = write_memory_bpw;
+
+ return err;
+}
+
+static int panel_mipi_read_firmware(const struct device *dev,
+ struct panel_mipi *mipi,
+ const struct panel_firmware *firmware)
+{
+ int rotation;
+ int err;
+
+ err = panel_mipi_load_commands(mipi, firmware->commands.data,
+ firmware->commands.size);
+ if (err) {
+ dev_err(dev, "firmware: Malformed command sequence\n");
+ return err;
+ }
+
+ mipi->width_mm = be16_to_cpu(firmware->config->width_mm);
+ mipi->height_mm = be16_to_cpu(firmware->config->height_mm);
+
+ rotation = be16_to_cpu(firmware->config->rotation);
+ if (rotation == 0)
+ mipi->orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
+ else if (rotation == 90)
+ mipi->orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
+ else if (rotation == 180)
+ mipi->orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
+ else if (rotation == 270)
+ mipi->orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
+ else {
+ dev_err(dev, "firmware: Invalid rotation %u\n", rotation);
+ return -EINVAL;
+ }
+
+ mipi->reset_delay = be16_to_cpu(firmware->config->reset_delay);
+ mipi->init_delay = be16_to_cpu(firmware->config->init_delay);
+ mipi->sleep_delay = be16_to_cpu(firmware->config->sleep_delay);
+ mipi->backlight_delay = be16_to_cpu(firmware->config->backlight_delay);
+
+ mipi->bus_flags = be32_to_cpu(firmware->config->bus_flags);
+
+ return 0;
+}
+
+static struct panel_firmware *panel_mipi_load_firmware(struct device *dev)
+{
+ const struct firmware *blob;
+ const struct panel_firmware_header *hdr;
+ const struct panel_firmware_config *config;
+ const struct panel_firmware_panel_timing *timings;
+ struct panel_firmware *firmware;
+ const char *firmware_name;
+ size_t command_offset;
+ char filename[128];
+ int err;
+
+ err = of_property_read_string_index(dev->of_node, "compatible", 0,
+ &firmware_name);
+ if (err)
+ return ERR_PTR(err);
+
+ snprintf(filename, sizeof(filename), "panels/%s.panel", firmware_name);
+ err = request_firmware(&blob, filename, dev);
+ if (err)
+ return ERR_PTR(err);
+
+ hdr = (const struct panel_firmware_header *)blob->data;
+
+ if (blob->size < sizeof(*hdr)) {
+ dev_err(dev, "firmware: %s: file size %zu is too small\n",
+ filename, blob->size);
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (memcmp(hdr->magic, panel_firmware_magic, sizeof(hdr->magic))) {
+ dev_err(dev, "firmware: %s: Bad magic %15ph\n", filename,
+ hdr->magic);
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (hdr->file_format_version != 1) {
+ dev_err(dev, "firmware: %s: version %u is not supported\n",
+ filename, hdr->file_format_version);
+ err = -EINVAL;
+ goto err;
+ }
+
+ config = (struct panel_firmware_config *)(blob->data + sizeof(*hdr));
+ timings = (struct panel_firmware_panel_timing *)(blob->data +
+ sizeof(*hdr) +
+ sizeof(*config));
+ command_offset = sizeof(*hdr) + sizeof(*config) +
+ config->num_timings * sizeof(*timings);
+ if (blob->size < command_offset) {
+ dev_err(dev, "firmware: %s: file size %zu is too small\n",
+ filename, blob->size);
+ err = -EINVAL;
+ goto err;
+ }
+
+ firmware = devm_kzalloc(dev, sizeof(*firmware), GFP_KERNEL);
+ if (!firmware) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ firmware->blob = blob;
+ firmware->config = config;
+ firmware->timings = timings;
+ firmware->commands.data = blob->data + command_offset;
+ firmware->commands.size = blob->size - command_offset;
+
+ dev_info(dev, "firmware: %s: loaded successfully\n", filename);
+
+ return firmware;
+
+err:
+ release_firmware(blob);
+
+ return ERR_PTR(err);
+}
+
+static int panel_mipi_read_display_id(struct panel_mipi *mipi)
+{
+ u8 buf[3];
+ int err;
+
+ err = panel_mipi_read(mipi, MIPI_DCS_GET_DISPLAY_ID, buf, sizeof(buf));
+
+ return err ? err : ((buf[0] << 16) | (buf[1] << 8) | buf[2]);
+}
+
+static int panel_mipi_enable(struct drm_panel *panel)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ int err;
+
+ PANEL_MIPI_TRANSACTION(mipi, {
+ err = panel_mipi_write(mipi, MIPI_DCS_SET_DISPLAY_ON);
+ });
+ if (err)
+ return err;
+
+ if (panel->backlight) {
+ /* Wait for the picture to be ready before enabling backlight */
+ msleep(mipi->backlight_delay);
+ }
+
+ return 0;
+}
+
+static int panel_mipi_disable(struct drm_panel *panel)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ int err = 0;
+
+ PANEL_MIPI_TRANSACTION(mipi, {
+ err = panel_mipi_write(mipi, MIPI_DCS_SET_DISPLAY_OFF);
+ });
+
+ return err;
+}
+
+static int panel_mipi_prepare(struct drm_panel *panel)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ int err, display_id;
+
+ err = regulator_bulk_enable(ARRAY_SIZE(mipi->supplies), mipi->supplies);
+ if (err)
+ return err;
+
+ PANEL_MIPI_TRANSACTION(mipi, {
+ /* Reset the chip */
+ if (mipi->reset) {
+ gpiod_set_value_cansleep(mipi->reset, 1);
+ msleep(mipi->reset_delay);
+ gpiod_set_value_cansleep(mipi->reset, 0);
+ }
+
+ msleep(mipi->init_delay);
+
+ /* Read the Display ID */
+ mipi->display_id = panel_mipi_read_display_id(mipi);
+ if (mipi->display_id >= 0)
+ dev_info(panel->dev, "MIPI Display ID: %06x\n",
+ mipi->display_id);
+ else
+ dev_warn(panel->dev, "MIPI Display ID: Unknown\n");
+
+ /* Write the init-sequence */
+ err = panel_mipi_write_commands(mipi, mipi->commands.data,
+ mipi->commands.size);
+ if (err)
+ break;
+
+ /* Ensure to exit from a sleep mode */
+ err = panel_mipi_write(mipi, MIPI_DCS_EXIT_SLEEP_MODE);
+ if (err)
+ break;
+ msleep(mipi->sleep_delay);
+
+ /*
+ * Read the Display ID again,
+ * because the init-sequence might have changed the ID.
+ */
+ display_id = panel_mipi_read_display_id(mipi);
+ if (display_id >= 0 && display_id != mipi->display_id)
+ dev_info(panel->dev, "MIPI Display ID changed: %06x\n",
+ display_id);
+ });
+
+ if (err) {
+ dev_err(panel->dev, "Failed to prepare the panel\n");
+ regulator_bulk_disable(ARRAY_SIZE(mipi->supplies),
+ mipi->supplies);
+ return err;
+ }
+
+ return 0;
+}
+
+static int panel_mipi_unprepare(struct drm_panel *panel)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ int err;
+
+ PANEL_MIPI_TRANSACTION(mipi, {
+ err = panel_mipi_write(mipi, MIPI_DCS_ENTER_SLEEP_MODE);
+ if (err)
+ dev_warn(panel->dev, "Failed to enter a sleep mode\n");
+ msleep(mipi->sleep_delay);
+ });
+
+ if (mipi->reset) {
+ gpiod_set_value_cansleep(mipi->reset, 1);
+ msleep(mipi->reset_delay);
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(mipi->supplies), mipi->supplies);
+
+ return 0;
+}
+
+static int panel_mipi_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ struct drm_display_mode *mode;
+ unsigned int count = 0;
+
+ list_for_each_entry(mode, &mipi->mode_list, head) {
+ struct drm_display_mode *dmode;
+
+ dmode = drm_mode_duplicate(connector->dev, mode);
+ if (!dmode)
+ return -EINVAL;
+
+ drm_mode_probed_add(connector, dmode);
+ count++;
+ }
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = mipi->width_mm;
+ connector->display_info.height_mm = mipi->height_mm;
+ connector->display_info.bus_flags = mipi->bus_flags;
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &mipi->bus_format, 1);
+
+ /*
+ * TODO: Remove once all drm drivers call
+ * drm_connector_set_orientation_from_panel()
+ */
+ drm_connector_set_panel_orientation(connector, mipi->orientation);
+
+ return count;
+}
+
+static enum drm_panel_orientation
+panel_mipi_get_orientation(struct drm_panel *panel)
+{
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+
+ return mipi->orientation;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int display_id_show(struct seq_file *s, void *data)
+{
+ struct drm_panel *panel = s->private;
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+ int display_id;
+
+ PANEL_MIPI_TRANSACTION(mipi, {
+ display_id = panel_mipi_read_display_id(mipi);
+ });
+
+ if (display_id >= 0)
+ seq_printf(s, "%06x\n", display_id);
+ else
+ seq_puts(s, "Unknown\n");
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(display_id);
+
+static int initial_display_id_show(struct seq_file *s, void *data)
+{
+ struct drm_panel *panel = s->private;
+ struct panel_mipi *mipi = to_panel_mipi(panel);
+
+ if (mipi->display_id >= 0)
+ seq_printf(s, "%06x\n", mipi->display_id);
+ else
+ seq_puts(s, "Unknown\n");
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(initial_display_id);
+
+static int commands_open(struct inode *inode, struct file *file)
+{
+ struct panel_mipi *mipi = inode->i_private;
+
+ mipi->recvlen = 0;
+ file->private_data = mipi;
+
+ return 0;
+}
+
+static ssize_t commands_write(struct file *file, const char __user *data,
+ size_t size, loff_t *pos)
+{
+ struct panel_mipi *mipi = file->private_data;
+ ssize_t s = min_t(u64, sizeof(mipi->recvbuf) - mipi->recvlen, size);
+ size_t valid_len;
+ int err;
+
+ if (!s || !mipi)
+ return -EINVAL;
+
+ if (copy_from_user(mipi->recvbuf + mipi->recvlen, data, s))
+ return -EINVAL;
+
+ mipi->recvlen += s;
+
+ err = panel_mipi_validate_commands(mipi, mipi->recvbuf, mipi->recvlen,
+ &valid_len);
+ if (err)
+ return err;
+
+ if (valid_len > 0) {
+ PANEL_MIPI_TRANSACTION(mipi, {
+ err = panel_mipi_write_commands(mipi, mipi->recvbuf,
+ valid_len);
+ });
+ if (err)
+ return err;
+
+ mipi->recvlen -= valid_len;
+ if (mipi->recvlen)
+ memmove(mipi->recvbuf, mipi->recvbuf + valid_len,
+ mipi->recvlen);
+ }
+
+ return s;
+}
+
+static int commands_release(struct inode *inode, struct file *file)
+{
+ struct panel_mipi *mipi = inode->i_private;
+
+ if (mipi->recvlen) {
+ dev_err(mipi->panel.dev, "Premature end of commands\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct file_operations commands_fops = {
+ .owner = THIS_MODULE,
+ .open = commands_open,
+ .write = commands_write,
+ .release = commands_release,
+};
+
+#endif
+
+static void panel_mipi_debugfs_init(struct device *dev, struct panel_mipi *mipi)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+
+ mipi->debugfs = debugfs_lookup("panel-mipi", NULL);
+ if (!mipi->debugfs)
+ mipi->debugfs = debugfs_create_dir("panel-mipi", NULL);
+
+ dir = debugfs_create_dir(dev_name(dev), mipi->debugfs);
+ debugfs_create_file("display-id", 0600, dir, mipi, &display_id_fops);
+ debugfs_create_file("initial-display-id", 0600, dir, mipi,
+ &initial_display_id_fops);
+ debugfs_create_file("commands", 0600, dir, mipi, &commands_fops);
+#endif
+}
+
+static void panel_mipi_debugfs_remove(struct panel_mipi *mipi)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(mipi->debugfs);
+ mipi->debugfs = NULL;
+#endif
+}
+
+static const struct drm_panel_funcs panel_mipi_funcs = {
+ .prepare = panel_mipi_prepare,
+ .unprepare = panel_mipi_unprepare,
+ .enable = panel_mipi_enable,
+ .disable = panel_mipi_disable,
+ .get_modes = panel_mipi_get_modes,
+ .get_orientation = panel_mipi_get_orientation,
+};
+
+static int panel_mipi_add_mode(struct device *dev, struct panel_mipi *mipi,
+ const struct drm_display_mode *src_mode)
+{
+ struct drm_display_mode *mode;
+
+ mode = devm_kzalloc(dev, sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_copy(mode, src_mode);
+
+ if (!mode->clock)
+ mode->clock = mode->htotal * mode->vtotal * 60 / 1000;
+
+ mode->type |= DRM_MODE_TYPE_DRIVER;
+
+ dev_info(dev, "Modeline " DRM_MODE_FMT " added\n", DRM_MODE_ARG(mode));
+
+ list_add_tail(&mode->head, &mipi->mode_list);
+
+ return 0;
+}
+
+static int panel_mipi_firmware_read_modes(const struct panel_firmware *firmware,
+ struct drm_display_mode *mode,
+ unsigned int i)
+{
+ const struct panel_firmware_panel_timing *timings =
+ &firmware->timings[i];
+
+ if (!firmware || i >= firmware->config->num_timings)
+ return -ENOENT;
+
+ if (!timings->hactive || !timings->vactive)
+ return -ENOENT;
+
+ memset(mode, 0, sizeof(*mode));
+
+ mode->clock = be32_to_cpu(timings->dclk);
+
+ mode->hdisplay = be16_to_cpu(timings->hactive);
+ mode->hsync_start = mode->hdisplay + be16_to_cpu(timings->hfp);
+ mode->hsync_end = mode->hsync_start + be16_to_cpu(timings->hslen);
+ mode->htotal = mode->hsync_end + be16_to_cpu(timings->hbp);
+
+ mode->vdisplay = be16_to_cpu(timings->vactive);
+ mode->vsync_start = mode->vdisplay + be16_to_cpu(timings->vfp);
+ mode->vsync_end = mode->vsync_start + be16_to_cpu(timings->vslen);
+ mode->vtotal = mode->vsync_end + be16_to_cpu(timings->vbp);
+
+ mode->flags = be32_to_cpu(timings->flags);
+
+ drm_mode_set_name(mode);
+
+ return 0;
+}
+
+static int panel_mipi_probe_modes(struct device *dev, struct panel_mipi *mipi)
+{
+ unsigned int i = 0;
+ struct drm_display_mode mode = {};
+ int err;
+
+ INIT_LIST_HEAD(&mipi->mode_list);
+
+ /* Read from the firmware */
+ for (i = 0; i < mipi->firmware->config->num_timings; i++) {
+ if (!panel_mipi_firmware_read_modes(mipi->firmware, &mode, i)) {
+ if (mipi->firmware->config->preferred_timing == i)
+ mode.type |= DRM_MODE_TYPE_PREFERRED;
+
+ err = panel_mipi_add_mode(dev, mipi, &mode);
+ if (err)
+ return err;
+ }
+ }
+
+ if (list_empty(&mipi->mode_list))
+ return dev_err_probe(dev, -EINVAL, "No modes defined\n");
+
+ return 0;
+}
+
+static void panel_mipi_cleanup(void *data)
+{
+ struct panel_mipi *mipi = (struct panel_mipi *)data;
+
+ panel_mipi_debugfs_remove(mipi);
+
+ drm_panel_remove(&mipi->panel);
+
+ if (mipi->panel.enabled)
+ drm_panel_disable(&mipi->panel);
+
+ if (mipi->panel.prepared)
+ drm_panel_unprepare(&mipi->panel);
+
+ if (mipi->firmware)
+ release_firmware(mipi->firmware->blob);
+}
+
+static int
+panel_mipi_backlight_update_status(struct backlight_device *backlight)
+{
+ struct panel_mipi *mipi = dev_get_drvdata(&backlight->dev);
+ int level = backlight_get_brightness(backlight);
+ int err;
+
+ PANEL_MIPI_TRANSACTION_LPM(mipi, false, {
+ err = panel_mipi_write(mipi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+ level, level);
+ });
+
+ return err;
+}
+
+static int
+panel_mipi_backlight_get_brightness(struct backlight_device *backlight)
+{
+ return backlight_get_brightness(backlight);
+}
+
+static const struct backlight_ops panel_mipi_backlight_ops = {
+ .get_brightness = panel_mipi_backlight_get_brightness,
+ .update_status = panel_mipi_backlight_update_status,
+};
+
+static int panel_mipi_set_backlight(struct drm_panel *panel, struct device *dev,
+ struct panel_mipi *mipi)
+{
+ int err;
+
+ err = drm_panel_of_backlight(panel);
+ if (err)
+ return err;
+
+ if (!panel->backlight) {
+ struct backlight_device *backlight = NULL;
+ struct backlight_properties props = {
+ .max_brightness = 255,
+ .type = BACKLIGHT_RAW,
+ };
+
+ backlight = devm_backlight_device_register(
+ dev, dev_name(dev), dev, mipi,
+ &panel_mipi_backlight_ops, &props);
+ if (IS_ERR(backlight))
+ return PTR_ERR(backlight);
+
+ panel->backlight = backlight;
+ }
+
+ return 0;
+}
+
+static int panel_mipi_probe(struct device *dev, int connector_type)
+{
+ struct panel_mipi *mipi;
+ int err;
+
+ mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+ if (!mipi)
+ return -ENOMEM;
+
+ mutex_init(&mipi->lock);
+
+ mipi->display_id = -1;
+
+ /* Get `power-supply` and `io-supply` (if any) */
+ mipi->supplies[0].supply = "power";
+ mipi->supplies[1].supply = "io";
+ err = devm_regulator_bulk_get(dev, ARRAY_SIZE(mipi->supplies),
+ mipi->supplies);
+ if (err < 0) {
+ return dev_err_probe(dev, err,
+ "%pOF: Failed to get regulators\n",
+ dev->of_node);
+ }
+
+ /* GPIO for /RESET */
+ mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(mipi->reset))
+ return dev_err_probe(dev, PTR_ERR(mipi->reset),
+ "%pOF: Failed to get GPIO for RESET\n",
+ dev->of_node);
+
+ /* Load the firmware */
+ mipi->firmware = panel_mipi_load_firmware(dev);
+ if (IS_ERR(mipi->firmware))
+ return dev_err_probe(dev, PTR_ERR(mipi->firmware),
+ "Failed to load firmware\n");
+
+ err = panel_mipi_read_firmware(dev, mipi, mipi->firmware);
+ if (err)
+ return err;
+
+ err = panel_mipi_probe_modes(dev, mipi);
+ if (err)
+ return err;
+
+ /* DRM panel setup */
+ drm_panel_init(&mipi->panel, dev, &panel_mipi_funcs, connector_type);
+
+ err = panel_mipi_set_backlight(&mipi->panel, dev, mipi);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to set backlight\n");
+
+ drm_panel_add(&mipi->panel);
+
+ dev_set_drvdata(dev, mipi);
+
+ panel_mipi_debugfs_init(dev, mipi);
+
+ return devm_add_action_or_reset(dev, panel_mipi_cleanup, mipi);
+}
+
+static int panel_mipi_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ struct panel_mipi *mipi;
+ int err;
+
+ err = panel_mipi_probe(&dsi->dev, DRM_MODE_CONNECTOR_DSI);
+ if (err)
+ return err;
+
+ mipi = dev_get_drvdata(&dsi->dev);
+ mipi->dsi = dsi;
+ mipi->write_command = panel_mipi_dsi_write;
+ mipi->read_command = panel_mipi_dsi_read;
+
+ /* Read from the firmware */
+ dsi->lanes = be16_to_cpu(mipi->firmware->config->dsi_lanes);
+ dsi->format = be16_to_cpu(mipi->firmware->config->dsi_format);
+ dsi->mode_flags = be32_to_cpu(mipi->firmware->config->dsi_mode_flags);
+
+ if (!dsi->lanes)
+ return dev_err_probe(&dsi->dev, -EINVAL,
+ "dsi-lanes == 0 for DSI panel\n");
+
+ /* Adjust bus_format */
+ switch (dsi->format) {
+ case MIPI_DSI_FMT_RGB888:
+ mipi->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case MIPI_DSI_FMT_RGB666:
+ mipi->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
+ break;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ mipi->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+ break;
+ case MIPI_DSI_FMT_RGB565:
+ mipi->bus_format = MEDIA_BUS_FMT_RGB565_1X16;
+ break;
+ }
+
+ err = mipi_dsi_attach(dsi);
+ if (err)
+ return dev_err_probe(&dsi->dev, err,
+ "Failed to attach to DSI\n");
+
+ return 0;
+}
+
+static int panel_mipi_spi_probe(struct spi_device *spi)
+{
+ struct panel_mipi *mipi;
+ struct gpio_desc *dc;
+ int err;
+
+ err = panel_mipi_probe(&spi->dev, DRM_MODE_CONNECTOR_DPI);
+ if (err)
+ return err;
+
+ mipi = dev_get_drvdata(&spi->dev);
+ mipi->write_command = panel_mipi_dbi_write;
+ mipi->read_command = panel_mipi_dbi_read;
+
+ dc = devm_gpiod_get_optional(&spi->dev, "dc", GPIOD_OUT_LOW);
+ if (IS_ERR(dc))
+ return dev_err_probe(&spi->dev, PTR_ERR(dc),
+ "Failed to get GPIO for D/CX\n");
+
+ err = mipi_dbi_spi_init(spi, &mipi->dbi, dc);
+ if (err)
+ return dev_err_probe(&spi->dev, err, "Failed to init SPI\n");
+
+ return 0;
+}
+
+static void panel_mipi_dsi_remove(struct mipi_dsi_device *dsi)
+{
+ mipi_dsi_detach(dsi);
+}
+
+static const struct of_device_id panel_mipi_dsi_of_match[] = {
+ { .compatible = "panel-mipi-dsi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, panel_mipi_dsi_of_match);
+
+static const struct of_device_id panel_mipi_spi_of_match[] = {
+ { .compatible = "panel-mipi-dpi-spi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, panel_mipi_spi_of_match);
+
+static const struct spi_device_id panel_mipi_spi_ids[] = {
+ { "panel-mipi-dpi-spi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, panel_mipi_spi_ids);
+
+static struct mipi_dsi_driver panel_mipi_dsi_driver = {
+ .probe = panel_mipi_dsi_probe,
+ .remove = panel_mipi_dsi_remove,
+ .driver = {
+ .name = "panel-mipi",
+ .of_match_table = panel_mipi_dsi_of_match,
+ },
+};
+
+static struct spi_driver panel_mipi_spi_driver = {
+ .probe = panel_mipi_spi_probe,
+ .id_table = panel_mipi_spi_ids,
+ .driver = {
+ .name = "panel-mipi",
+ .of_match_table = panel_mipi_spi_of_match,
+ },
+};
+
+static int __init panel_mipi_driver_init(void)
+{
+ int err;
+
+ if (IS_ENABLED(CONFIG_SPI)) {
+ err = spi_register_driver(&panel_mipi_spi_driver);
+ if (err)
+ return err;
+ }
+
+ if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
+ err = mipi_dsi_driver_register(&panel_mipi_dsi_driver);
+ if (err) {
+ if (IS_ENABLED(CONFIG_SPI))
+ spi_unregister_driver(&panel_mipi_spi_driver);
+ return err;
+ }
+ }
+
+ return 0;
+}
+module_init(panel_mipi_driver_init);
+
+static void __exit panel_mipi_driver_exit(void)
+{
+ if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+ mipi_dsi_driver_unregister(&panel_mipi_dsi_driver);
+
+ if (IS_ENABLED(CONFIG_SPI))
+ spi_unregister_driver(&panel_mipi_spi_driver);
+}
+module_exit(panel_mipi_driver_exit);
+
+MODULE_DESCRIPTION("Generic MIPI-DSI/DPI(+SPI) Panel Driver");
+MODULE_AUTHOR("Hironori KIKUCHI <kikuchan98@gmail.com>");
+MODULE_LICENSE("GPL");
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels
2025-02-26 11:25 ` [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels Hironori KIKUCHI
@ 2025-03-03 9:26 ` Krzysztof Kozlowski
2025-03-06 7:53 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: Krzysztof Kozlowski @ 2025-03-03 9:26 UTC (permalink / raw)
To: Hironori KIKUCHI
Cc: linux-kernel, Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jakub Kicinski,
dri-devel, devicetree
On Wed, Feb 26, 2025 at 08:25:50PM +0900, Hironori KIKUCHI wrote:
> +
> +struct panel_firmware_header {
> + u8 magic[15];
> + u8 file_format_version; /* must be 1 */
> +} __packed;
> +
> +struct panel_firmware_config {
> + u16 width_mm, height_mm;
> + u16 rotation;
> + u8 _reserved_1[2];
> + u8 _reserved_2[8];
> +
> + u16 reset_delay; /* delay after the reset command, in ms */
> + u16 init_delay; /* delay for sending the initial command sequence, in ms */
> + u16 sleep_delay; /* delay after the sleep command, in ms */
> + u16 backlight_delay; /* delay for enabling the backlight, in ms */
These should be implied by compatible.
> + u16 _reserved_3[4];
> +
> + u16 dsi_lanes; /* unsigned int */
> + u16 dsi_format; /* enum mipi_dsi_pixel_format */
> + u32 dsi_mode_flags; /* unsigned long */
> + u32 bus_flags; /* struct drm_bus_flags */
> + u8 _reserved_4[2];
> + u8 preferred_timing;
> + u8 num_timings;
> +} __packed;
> +
...
> +
> +static int panel_mipi_probe(struct device *dev, int connector_type)
> +{
> + struct panel_mipi *mipi;
> + int err;
> +
> + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
> + if (!mipi)
> + return -ENOMEM;
> +
> + mutex_init(&mipi->lock);
> +
> + mipi->display_id = -1;
> +
> + /* Get `power-supply` and `io-supply` (if any) */
> + mipi->supplies[0].supply = "power";
> + mipi->supplies[1].supply = "io";
> + err = devm_regulator_bulk_get(dev, ARRAY_SIZE(mipi->supplies),
> + mipi->supplies);
> + if (err < 0) {
> + return dev_err_probe(dev, err,
> + "%pOF: Failed to get regulators\n",
> + dev->of_node);
Drop pOF. Device name already tells this.
> + }
> +
> + /* GPIO for /RESET */
> + mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(mipi->reset))
> + return dev_err_probe(dev, PTR_ERR(mipi->reset),
> + "%pOF: Failed to get GPIO for RESET\n",
Drop pOF. Device name already tells this.
> + dev->of_node);
> +
> + /* Load the firmware */
> + mipi->firmware = panel_mipi_load_firmware(dev);
> + if (IS_ERR(mipi->firmware))
> + return dev_err_probe(dev, PTR_ERR(mipi->firmware),
> + "Failed to load firmware\n");
> +
> + err = panel_mipi_read_firmware(dev, mipi, mipi->firmware);
> + if (err)
> + return err;
> +
> + err = panel_mipi_probe_modes(dev, mipi);
> + if (err)
> + return err;
> +
> + /* DRM panel setup */
> + drm_panel_init(&mipi->panel, dev, &panel_mipi_funcs, connector_type);
> +
> + err = panel_mipi_set_backlight(&mipi->panel, dev, mipi);
> + if (err)
> + return dev_err_probe(dev, err, "Failed to set backlight\n");
> +
> + drm_panel_add(&mipi->panel);
> +
> + dev_set_drvdata(dev, mipi);
> +
> + panel_mipi_debugfs_init(dev, mipi);
> +
> + return devm_add_action_or_reset(dev, panel_mipi_cleanup, mipi);
> +}
> +
> +static int panel_mipi_dsi_probe(struct mipi_dsi_device *dsi)
> +{
> + struct panel_mipi *mipi;
> + int err;
> +
> + err = panel_mipi_probe(&dsi->dev, DRM_MODE_CONNECTOR_DSI);
> + if (err)
> + return err;
> +
> + mipi = dev_get_drvdata(&dsi->dev);
> + mipi->dsi = dsi;
> + mipi->write_command = panel_mipi_dsi_write;
> + mipi->read_command = panel_mipi_dsi_read;
> +
> + /* Read from the firmware */
> + dsi->lanes = be16_to_cpu(mipi->firmware->config->dsi_lanes);
lanes are from DT, because they are depending on how panel is wired. At
least usually.
> + dsi->format = be16_to_cpu(mipi->firmware->config->dsi_format);
> + dsi->mode_flags = be32_to_cpu(mipi->firmware->config->dsi_mode_flags);
> +
> + if (!dsi->lanes)
> + return dev_err_probe(&dsi->dev, -EINVAL,
> + "dsi-lanes == 0 for DSI panel\n");
> +
> + /* Adjust bus_format */
> + switch (dsi->format) {
> + case MIPI_DSI_FMT_RGB888:
> + mipi->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> + case MIPI_DSI_FMT_RGB666:
> + mipi->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
> + break;
> + case MIPI_DSI_FMT_RGB666_PACKED:
> + mipi->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
> + break;
> + case MIPI_DSI_FMT_RGB565:
> + mipi->bus_format = MEDIA_BUS_FMT_RGB565_1X16;
> + break;
> + }
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix
2025-02-26 11:25 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix Hironori KIKUCHI
@ 2025-03-03 13:06 ` Rob Herring (Arm)
0 siblings, 0 replies; 8+ messages in thread
From: Rob Herring (Arm) @ 2025-03-03 13:06 UTC (permalink / raw)
To: Hironori KIKUCHI
Cc: Conor Dooley, Jakub Kicinski, Thomas Zimmermann, Jessica Zhang,
Simona Vetter, Krzysztof Kozlowski, Maarten Lankhorst,
linux-kernel, dri-devel, Maxime Ripard, devicetree,
Neil Armstrong, David Airlie
On Wed, 26 Feb 2025 20:25:48 +0900, Hironori KIKUCHI wrote:
> Add prefix for Hotdisplay Technology Co.Ltd
>
> Signed-off-by: Hironori KIKUCHI <kikuchan98@gmail.com>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
> 1 file changed, 2 insertions(+)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels
2025-02-26 11:25 ` [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels Hironori KIKUCHI
@ 2025-03-03 13:12 ` Rob Herring
0 siblings, 0 replies; 8+ messages in thread
From: Rob Herring @ 2025-03-03 13:12 UTC (permalink / raw)
To: Hironori KIKUCHI
Cc: linux-kernel, Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Krzysztof Kozlowski, Conor Dooley, Jakub Kicinski, dri-devel,
devicetree
On Wed, Feb 26, 2025 at 08:25:49PM +0900, Hironori KIKUCHI wrote:
> This is a binding for generic MIPI-DSI/DPI panels that require
> initialization with a simple command sequence before use.
>
> The initialization of the panel requires a firmware binary which can be
> made with the Panel Firmware Generator[1] on the web.
>
> Add 4 new panels, as they are available on the same page[1] as a preset
> (excluding already included ones).
>
> Note that the "xx" in the panel name is taken from a product's name,
> not a wildcard.
>
> [1]: https://kikuchan.github.io/panel-firmware-generator/
>
> Signed-off-by: Hironori KIKUCHI <kikuchan98@gmail.com>
> ---
> .../bindings/display/panel/panel-mipi.yaml | 121 ++++++++++++++++++
> 1 file changed, 121 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
>
> diff --git a/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml b/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
> new file mode 100644
> index 00000000000..d70cf0063fa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/panel-mipi.yaml
> @@ -0,0 +1,121 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/panel/panel-mipi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Generic MIPI-DSI/DPI Panels Requiring Initialization
> +
> +maintainers:
> + - Hironori KIKUCHI <kikuchan98@gmail.com>
> +
> +description: This is a binding for generic MIPI-DSI/DPI panels that require
> + initialization with a simple command sequence before use.
> +
> +properties:
> + compatible:
> + oneOf:
> + - items:
> + - enum:
> + # Unknown 3.35" 720x480 24-bit IPS LCD panel
> + # used in Anbernic RG 34XX
> + - anbernic,rg34xx-panel
> + # Unknown 4.00" 640x480 24-bit IPS LCD panel (YLM-LBV0400001X-V1)
> + # used in Anbernic RG40XX series
> + - anbernic,rg40xx-panel
> + # Unknown 3.95" 720x720 24-bit IPS LCD panel (YLM-LBN0395004H-V1)
> + # used in Anbernic RG CubeXX
> + - anbernic,rgcubexx-panel
> + - const: panel-mipi-dpi-spi
We already have a schema for this: panel-mipi-dpi-spi.yaml
> + - items:
> + - enum:
> + # HOTHMI TFT-H028B23VGIST7G40-V1 2.80" 480x640 TFT LCD panel
> + - hothmi,tft-h028b23vgist7g40-v1
> + - const: panel-mipi-dsi
We have lots of DSI panels already and they don't have a generic
fallback. What exactly would "panel-mipi-dsi" mean to the OS? It's got
to be useful on its own or it should be dropped.
> +
> + reg:
> + description: DSI / SPI channel used by that screen
> + maxItems: 1
> +
> + power-supply: true
> +
> + io-supply:
> + description: I/O system regulator.
> + No need to set if this is the same as polwer-supply.
> +
> + dc-gpios:
> + maxItems: 1
> + description: Controller data/command selection (D/CX) in 4-line SPI mode.
> + If not set, the controller is in 3-line SPI mode.
> + Disallowed for DSI.
> +
> + port: true
> + reset-gpios: true
> +
> + backlight: true
> +
> +required:
> + - compatible
> + - reg
> +
> +allOf:
> + - $ref: panel-common.yaml#
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - panel-mipi-dpi-spi
> + then:
> + # SPI mode
> + $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - panel-mipi-dsi
> + then:
> + # DSI mode
> + properties:
> + dc-gpios: false
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + dsi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + panel@0 {
> + compatible = "hothmi,tft-h028b23vgist7g40-v1", "panel-mipi-dsi";
> + reg = <0>;
> +
> + port {
> + mipi_in_panel: endpoint {
> + remote-endpoint = <&mipi_out_panel>;
> + };
> + };
> + };
> + };
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + panel@0 {
> + compatible = "anbernic,rg40xx-panel", "panel-mipi-dpi-spi";
> + reg = <0>;
> +
> + spi-max-frequency = <40000000>;
> +
> + dc-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
> + reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
> +
> + backlight = <&backlight>;
> + };
> + };
> --
> 2.48.1
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels
2025-02-26 11:25 ` [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels Hironori KIKUCHI
2025-03-03 9:26 ` Krzysztof Kozlowski
@ 2025-03-06 7:53 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-03-06 7:53 UTC (permalink / raw)
To: Hironori KIKUCHI, linux-kernel
Cc: oe-kbuild-all, Hironori KIKUCHI, Neil Armstrong, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jakub Kicinski, dri-devel, devicetree
Hi Hironori,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.14-rc5 next-20250305]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Hironori-KIKUCHI/dt-bindings-vendor-prefixes-Add-hothmi-vendor-prefix/20250226-192724
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20250226112552.52494-4-kikuchan98%40gmail.com
patch subject: [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels
config: csky-randconfig-r112-20250306 (https://download.01.org/0day-ci/archive/20250306/202503061541.2JX2lTlc-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 14.2.0
reproduce: (https://download.01.org/0day-ci/archive/20250306/202503061541.2JX2lTlc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503061541.2JX2lTlc-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/gpu/drm/panel/panel-mipi.c:407:26: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:408:27: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:410:20: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:424:29: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:425:28: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:426:29: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:427:33: sparse: sparse: cast to restricted __be16
>> drivers/gpu/drm/panel/panel-mipi.c:429:27: sparse: sparse: cast to restricted __be32
drivers/gpu/drm/panel/panel-mipi.c:854:23: sparse: sparse: cast to restricted __be32
drivers/gpu/drm/panel/panel-mipi.c:856:26: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:857:46: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:858:47: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:859:42: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:861:26: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:862:46: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:863:47: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:864:42: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:866:23: sparse: sparse: cast to restricted __be32
drivers/gpu/drm/panel/panel-mipi.c:1047:22: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:1048:23: sparse: sparse: cast to restricted __be16
drivers/gpu/drm/panel/panel-mipi.c:1049:27: sparse: sparse: cast to restricted __be32
drivers/gpu/drm/panel/panel-mipi.c: note: in included file (through include/linux/mutex.h, include/drm/drm_mipi_dbi.h):
include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
vim +407 drivers/gpu/drm/panel/panel-mipi.c
392
393 static int panel_mipi_read_firmware(const struct device *dev,
394 struct panel_mipi *mipi,
395 const struct panel_firmware *firmware)
396 {
397 int rotation;
398 int err;
399
400 err = panel_mipi_load_commands(mipi, firmware->commands.data,
401 firmware->commands.size);
402 if (err) {
403 dev_err(dev, "firmware: Malformed command sequence\n");
404 return err;
405 }
406
> 407 mipi->width_mm = be16_to_cpu(firmware->config->width_mm);
408 mipi->height_mm = be16_to_cpu(firmware->config->height_mm);
409
410 rotation = be16_to_cpu(firmware->config->rotation);
411 if (rotation == 0)
412 mipi->orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
413 else if (rotation == 90)
414 mipi->orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
415 else if (rotation == 180)
416 mipi->orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
417 else if (rotation == 270)
418 mipi->orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
419 else {
420 dev_err(dev, "firmware: Invalid rotation %u\n", rotation);
421 return -EINVAL;
422 }
423
424 mipi->reset_delay = be16_to_cpu(firmware->config->reset_delay);
425 mipi->init_delay = be16_to_cpu(firmware->config->init_delay);
426 mipi->sleep_delay = be16_to_cpu(firmware->config->sleep_delay);
427 mipi->backlight_delay = be16_to_cpu(firmware->config->backlight_delay);
428
> 429 mipi->bus_flags = be32_to_cpu(firmware->config->bus_flags);
430
431 return 0;
432 }
433
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-03-06 7:55 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-26 11:25 [PATCH v2 0/3] drm: panel: Add Generic MIPI Panel Driver Hironori KIKUCHI
2025-02-26 11:25 ` [PATCH v2 1/3] dt-bindings: vendor-prefixes: Add hothmi vendor prefix Hironori KIKUCHI
2025-03-03 13:06 ` Rob Herring (Arm)
2025-02-26 11:25 ` [PATCH v2 2/3] dt-bindings: display: panel: Add some generic MIPI-DSI/DPI panels Hironori KIKUCHI
2025-03-03 13:12 ` Rob Herring
2025-02-26 11:25 ` [PATCH v2 3/3] drm: panel: Add a driver for Generic MIPI-DSI/DPI(+SPI) panels Hironori KIKUCHI
2025-03-03 9:26 ` Krzysztof Kozlowski
2025-03-06 7:53 ` kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.