The LG SW43408 panel is used on Google Pixel3 devices. For a long time
we could not submit the driver, as the panel was not coming up from the
reset. The panel seems to be picky about some of the delays during init
and it also uses non-standard payload for MIPI_DSI_COMPRESSION_MODE.
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
Changes in v3:
- Fixed return type of MIPI DSC functions
- Replaced mipi_dsi_compression_mode_raw() with
mipi_dsi_compression_mode_ext() (Marijn)
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- Removed formatting char from schema (Krzysztof)
- Moved additionalProperties after required (Krzysztof)
- Added example to the schema (Krzysztof)
- Removed obsolete comment in the commit message (Marijn)
- Moved DSC params to the panel struct (Marijn)
- Changed dsc_en to be an array (Marijn)
- Added comment regiarding slice_width and slice_count (Marijn)
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Dmitry Baryshkov (2):
drm/mipi-dsi: use correct return type for the DSC functions
drm/mipi-dsi: add mipi_dsi_compression_mode_ext()
Sumit Semwal (2):
dt-bindings: panel: Add LG SW43408 MIPI-DSI panel
drm: panel: Add LG sw43408 panel driver
.../bindings/display/panel/lg,sw43408.yaml | 62 ++++
MAINTAINERS | 8 +
drivers/gpu/drm/drm_mipi_dsi.c | 37 ++-
drivers/gpu/drm/panel/Kconfig | 11 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-lg-sw43408.c | 326 +++++++++++++++++++++
include/drm/drm_mipi_dsi.h | 15 +-
7 files changed, 449 insertions(+), 11 deletions(-)
---
base-commit: a6bd6c9333397f5a0e2667d4d82fef8c970108f2
change-id: 20240330-lg-sw43408-panel-b552f411c53e
Best regards,
--
Dmitry Baryshkov <[email protected]>
From: Sumit Semwal <[email protected]>
LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
phones.
Signed-off-by: Vinod Koul <[email protected]>
Signed-off-by: Sumit Semwal <[email protected]>
[caleb: convert to yaml]
Signed-off-by: Caleb Connolly <[email protected]>
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
.../bindings/display/panel/lg,sw43408.yaml | 62 ++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml b/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
new file mode 100644
index 000000000000..1e08648f5bc7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/lg,sw43408.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LG SW43408 1080x2160 DSI panel
+
+maintainers:
+ - Caleb Connolly <[email protected]>
+
+description:
+ This panel is used on the Pixel 3, it is a 60hz OLED panel which
+ required DSC (Display Stream Compression) and has rounded corners.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: lg,sw43408
+
+ reg: true
+ port: true
+ vddi-supply: true
+ vpnl-supply: true
+ reset-gpios: true
+
+required:
+ - compatible
+ - vddi-supply
+ - vpnl-supply
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "lg,sw43408";
+ reg = <0>;
+
+ vddi-supply = <&vreg_l14a_1p88>;
+ vpnl-supply = <&vreg_l28a_3p0>;
+
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&mdss_dsi0_out>;
+ };
+ };
+ };
+ };
+...
--
2.39.2
The functions mipi_dsi_compression_mode() and
mipi_dsi_picture_parameter_set() return 0-or-error rather than a buffer
size. Follow example of other similar MIPI DSI functions and use int
return type instead of size_t.
Fixes: f4dea1aaa9a1 ("drm/dsi: add helpers for DSI compression mode and PPS packets")
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
drivers/gpu/drm/drm_mipi_dsi.c | 6 +++---
include/drm/drm_mipi_dsi.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index ef6e416522f8..9874ff6d4718 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -654,7 +654,7 @@ EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
*
* Return: 0 on success or a negative error code on failure.
*/
-ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
{
/* Note: Needs updating for non-default PPS or algorithm */
u8 tx[2] = { enable << 0, 0 };
@@ -679,8 +679,8 @@ EXPORT_SYMBOL(mipi_dsi_compression_mode);
*
* Return: 0 on success or a negative error code on failure.
*/
-ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
- const struct drm_dsc_picture_parameter_set *pps)
+int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
+ const struct drm_dsc_picture_parameter_set *pps)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index c0aec0d4d664..3011d33eccbd 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -241,9 +241,9 @@ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value);
-ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
-ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
- const struct drm_dsc_picture_parameter_set *pps);
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
+int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
+ const struct drm_dsc_picture_parameter_set *pps);
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size);
--
2.39.2
Add the extended version of mipi_dsi_compression_mode(). It provides
a way to specify the algorithm and PPS selector.
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
drivers/gpu/drm/drm_mipi_dsi.c | 33 +++++++++++++++++++++++++++------
include/drm/drm_mipi_dsi.h | 9 +++++++++
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 9874ff6d4718..0ecbc811eb7a 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -645,19 +645,24 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
/**
- * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
+ * mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
* @dsi: DSI peripheral device
* @enable: Whether to enable or disable the DSC
+ * @algo: Selected algorithm
+ * @pps_selector: The PPS selector
*
- * Enable or disable Display Stream Compression on the peripheral using the
- * default Picture Parameter Set and VESA DSC 1.1 algorithm.
+ * Enable or disable Display Stream Compression on the peripheral.
*
* Return: 0 on success or a negative error code on failure.
*/
-int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
+ enum mipi_dsi_compression_algo algo,
+ unsigned int pps_selector)
{
- /* Note: Needs updating for non-default PPS or algorithm */
- u8 tx[2] = { enable << 0, 0 };
+ u8 data = (enable << 0) |
+ (algo << 1) |
+ (pps_selector << 4);
+ u8 tx[2] = { data, 0 };
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_COMPRESSION_MODE,
@@ -668,6 +673,22 @@ int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
return (ret < 0) ? ret : 0;
}
+EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
+
+/**
+ * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
+ * @dsi: DSI peripheral device
+ * @enable: Whether to enable or disable the DSC
+ *
+ * Enable or disable Display Stream Compression on the peripheral using the
+ * default Picture Parameter Set and VESA DSC 1.1 algorithm.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+{
+ return mipi_dsi_compression_mode_ext(dsi, enable, 0, MIPI_DSI_COMPRESSION_DSC);
+}
EXPORT_SYMBOL(mipi_dsi_compression_mode);
/**
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 3011d33eccbd..78cb7b688b1d 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
return -EINVAL;
}
+enum mipi_dsi_compression_algo {
+ MIPI_DSI_COMPRESSION_DSC = 0,
+ MIPI_DSI_COMPRESSION_VENDOR = 3,
+ /* other two values are reserved, DSI 1.3 */
+};
+
struct mipi_dsi_device *
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
const struct mipi_dsi_device_info *info);
@@ -242,6 +248,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value);
int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
+int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
+ unsigned int pps_selector,
+ enum mipi_dsi_compression_algo algo);
int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
const struct drm_dsc_picture_parameter_set *pps);
--
2.39.2
From: Sumit Semwal <[email protected]>
LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
phones.
Signed-off-by: Sumit Semwal <[email protected]>
[vinod: Add DSC support]
Signed-off-by: Vinod Koul <[email protected]>
[caleb: cleanup and support turning off the panel]
Signed-off-by: Caleb Connolly <[email protected]>
[DB: partially rewrote the driver and fixed DSC programming]
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
MAINTAINERS | 8 +
drivers/gpu/drm/panel/Kconfig | 11 ++
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-lg-sw43408.c | 326 +++++++++++++++++++++++++++++++
4 files changed, 346 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d36c19c1bf81..4cc43c16e07e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6789,6 +6789,14 @@ S: Maintained
F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
+DRM DRIVER FOR LG SW43408 PANELS
+M: Sumit Semwal <[email protected]>
+M: Caleb Connolly <[email protected]>
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
+F: drivers/gpu/drm/panel/panel-lg-sw43408.c
+
DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
M: Paul Kocialkowski <[email protected]>
S: Supported
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 6dc451f58a3e..a55e9437c8cf 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here.
+config DRM_PANEL_LG_SW43408
+ tristate "LG SW43408 panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for LG sw43408 panel.
+ The panel has a 1080x2160 resolution and uses
+ 24 bit RGB per pixel. It provides a MIPI DSI interface to
+ the host and has a built-in LED backlight.
+
config DRM_PANEL_MAGNACHIP_D53E6EA8966
tristate "Magnachip D53E6EA8966 DSI panel"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 24a02655d726..0b40b010e8e7 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
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_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
diff --git a/drivers/gpu/drm/panel/panel-lg-sw43408.c b/drivers/gpu/drm/panel/panel-lg-sw43408.c
new file mode 100644
index 000000000000..c7611bfa796b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-sw43408.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-2024 Linaro Ltd
+ * Author: Sumit Semwal <[email protected]>
+ * Dmitry Baryshkov <[email protected]>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+
+#define NUM_SUPPLIES 2
+
+struct sw43408_panel {
+ struct drm_panel base;
+ struct mipi_dsi_device *link;
+
+ const struct drm_display_mode *mode;
+
+ struct regulator_bulk_data supplies[NUM_SUPPLIES];
+
+ struct gpio_desc *reset_gpio;
+
+ struct drm_dsc_config dsc;
+};
+
+static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
+{
+ return container_of(panel, struct sw43408_panel, base);
+}
+
+static int sw43408_unprepare(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ int ret;
+
+ ret = mipi_dsi_dcs_set_display_off(ctx->link);
+ if (ret < 0)
+ dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
+ if (ret < 0)
+ dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
+
+ msleep(100);
+
+ gpiod_set_value(ctx->reset_gpio, 1);
+
+ return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int sw43408_program(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ struct drm_dsc_picture_parameter_set pps;
+
+ mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
+
+ mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
+ mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
+
+ mipi_dsi_dcs_exit_sleep_mode(ctx->link);
+
+ msleep(135);
+
+ /* COMPRESSION_MODE moved after setting the PPS */
+
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
+ 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
+ 0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
+ 0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
+ 0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
+ 0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
+ 0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
+ 0x01);
+ msleep(85);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
+ 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x16, 0x16);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
+ mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
+
+ mipi_dsi_dcs_set_display_on(ctx->link);
+
+ msleep(50);
+
+ ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
+ mipi_dsi_picture_parameter_set(ctx->link, &pps);
+
+ /* This panel uses shifted PPS selectors:
+ * 1 if pps_identifier is 0
+ * 2 if pps_identifier is 1
+ */
+ mipi_dsi_compression_mode_ext(ctx->link, true,
+ MIPI_DSI_COMPRESSION_DSC, 1);
+
+ ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static int sw43408_prepare(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(9000, 10000);
+ gpiod_set_value(ctx->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(9000, 10000);
+
+ ret = sw43408_program(panel);
+ if (ret)
+ goto poweroff;
+
+ return 0;
+
+poweroff:
+ gpiod_set_value(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ return ret;
+}
+
+static int sw43408_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
+}
+
+static int sw43408_backlight_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ uint16_t brightness = backlight_get_brightness(bl);
+
+ return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+}
+
+const struct backlight_ops sw43408_backlight_ops = {
+ .update_status = sw43408_backlight_update_status,
+};
+
+static int sw43408_backlight_init(struct sw43408_panel *ctx)
+{
+ struct device *dev = &ctx->link->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 255,
+ .max_brightness = 255,
+ };
+
+ ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
+ ctx->link,
+ &sw43408_backlight_ops,
+ &props);
+
+ if (IS_ERR(ctx->base.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
+ "Failed to create backlight\n");
+
+ return 0;
+}
+
+static const struct drm_panel_funcs sw43408_funcs = {
+ .unprepare = sw43408_unprepare,
+ .prepare = sw43408_prepare,
+ .get_modes = sw43408_get_modes,
+};
+
+static const struct drm_display_mode sw43408_default_mode = {
+ .clock = 152340,
+
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 20,
+ .hsync_end = 1080 + 20 + 32,
+ .htotal = 1080 + 20 + 32 + 20,
+
+ .vdisplay = 2160,
+ .vsync_start = 2160 + 20,
+ .vsync_end = 2160 + 20 + 4,
+ .vtotal = 2160 + 20 + 4 + 20,
+
+ .width_mm = 62,
+ .height_mm = 124,
+
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct of_device_id sw43408_of_match[] = {
+ { .compatible = "lg,sw43408", .data = &sw43408_default_mode },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sw43408_of_match);
+
+static int sw43408_add(struct sw43408_panel *ctx)
+{
+ struct device *dev = &ctx->link->dev;
+ int ret;
+
+ ctx->supplies[0].supply = "vddi"; /* 1.88 V */
+ ctx->supplies[0].init_load_uA = 62000;
+ ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
+ ctx->supplies[1].init_load_uA = 857000;
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio)) {
+ dev_err(dev, "cannot get reset gpio %ld\n",
+ PTR_ERR(ctx->reset_gpio));
+ return PTR_ERR(ctx->reset_gpio);
+ }
+
+ ret = sw43408_backlight_init(ctx);
+ if (ret < 0)
+ return ret;
+
+ ctx->base.prepare_prev_first = true;
+
+ drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
+
+ drm_panel_add(&ctx->base);
+ return ret;
+}
+
+static int sw43408_probe(struct mipi_dsi_device *dsi)
+{
+ struct sw43408_panel *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->mode = of_device_get_match_data(&dsi->dev);
+ dsi->mode_flags = MIPI_DSI_MODE_LPM;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+
+ ctx->link = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ret = sw43408_add(ctx);
+ if (ret < 0)
+ return ret;
+
+ /* The panel is DSC panel only, set the dsc params */
+ ctx->dsc.dsc_version_major = 0x1;
+ ctx->dsc.dsc_version_minor = 0x1;
+
+ /* slice_count * slice_width == width */
+ ctx->dsc.slice_height = 16;
+ ctx->dsc.slice_width = 540;
+ ctx->dsc.slice_count = 2;
+ ctx->dsc.bits_per_component = 8;
+ ctx->dsc.bits_per_pixel = 8 << 4;
+ ctx->dsc.block_pred_enable = true;
+
+ dsi->dsc = &ctx->dsc;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static void sw43408_remove(struct mipi_dsi_device *dsi)
+{
+ struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = sw43408_unprepare(&ctx->base);
+ if (ret < 0)
+ dev_err(&dsi->dev, "failed to unprepare panel: %d\n",
+ ret);
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->base);
+}
+
+static struct mipi_dsi_driver sw43408_driver = {
+ .driver = {
+ .name = "panel-lg-sw43408",
+ .of_match_table = sw43408_of_match,
+ },
+ .probe = sw43408_probe,
+ .remove = sw43408_remove,
+};
+module_mipi_dsi_driver(sw43408_driver);
+
+MODULE_AUTHOR("Sumit Semwal <[email protected]>");
+MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
+MODULE_LICENSE("GPL");
--
2.39.2
On 02/04/2024 01:51, Dmitry Baryshkov wrote:
> From: Sumit Semwal <[email protected]>
>
> LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
> phones.
>
> Signed-off-by: Vinod Koul <[email protected]>
> Signed-off-by: Sumit Semwal <[email protected]>
> [caleb: convert to yaml]
> Signed-off-by: Caleb Connolly <[email protected]>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
Tags missing.
`b4 trailers -u`
Best regards,
Krzysztof
On Tue, 2 Apr 2024 at 09:31, Krzysztof Kozlowski
<[email protected]> wrote:
>
> On 02/04/2024 01:51, Dmitry Baryshkov wrote:
> > From: Sumit Semwal <[email protected]>
> >
> > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
> > phones.
> >
> > Signed-off-by: Vinod Koul <[email protected]>
> > Signed-off-by: Sumit Semwal <[email protected]>
> > [caleb: convert to yaml]
> > Signed-off-by: Caleb Connolly <[email protected]>
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
>
> Tags missing.
>
> `b4 trailers -u`
Excuse me, I keep on forgetting it.
--
With best wishes
Dmitry
On 2024-04-02 02:51:14, Dmitry Baryshkov wrote:
> Add the extended version of mipi_dsi_compression_mode(). It provides
> a way to specify the algorithm and PPS selector.
>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> drivers/gpu/drm/drm_mipi_dsi.c | 33 +++++++++++++++++++++++++++------
> include/drm/drm_mipi_dsi.h | 9 +++++++++
> 2 files changed, 36 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 9874ff6d4718..0ecbc811eb7a 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -645,19 +645,24 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
>
> /**
> - * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> + * mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
> * @dsi: DSI peripheral device
> * @enable: Whether to enable or disable the DSC
> + * @algo: Selected algorithm
> + * @pps_selector: The PPS selector
Not a big fan of paraphrasing the parameter name, it adds no value. How about
describing what this parameter means and what it does?:
PPS table index to use. Corresponds to a table pre-programmed on the peripheral
or a table programmed with &drm_dsc_picture_parameter_set.pps_identifier.
(That should be a valid kernel-doc cross-reference to the field)
> *
> - * Enable or disable Display Stream Compression on the peripheral using the
> - * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> + * Enable or disable Display Stream Compression on the peripheral.
> *
> * Return: 0 on success or a negative error code on failure.
> */
> -int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> + enum mipi_dsi_compression_algo algo,
> + unsigned int pps_selector)
> {
> - /* Note: Needs updating for non-default PPS or algorithm */
> - u8 tx[2] = { enable << 0, 0 };
> + u8 data = (enable << 0) |
> + (algo << 1) |
> + (pps_selector << 4);
Do we need some size validation (if > 3 return -EINVAL)? FIELD_PREP() might be
too heavy though.
> + u8 tx[2] = { data, 0 };
> struct mipi_dsi_msg msg = {
> .channel = dsi->channel,
> .type = MIPI_DSI_COMPRESSION_MODE,
> @@ -668,6 +673,22 @@ int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
>
> return (ret < 0) ? ret : 0;
> }
> +EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
> +
> +/**
> + * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> + * @dsi: DSI peripheral device
> + * @enable: Whether to enable or disable the DSC
> + *
> + * Enable or disable Display Stream Compression on the peripheral using the
> + * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> + *
> + * Return: 0 on success or a negative error code on failure.
> + */
> +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> +{
> + return mipi_dsi_compression_mode_ext(dsi, enable, 0, MIPI_DSI_COMPRESSION_DSC);
I hope the compiler complains here that it should be MIPI_DSI_COMPRESSION_DSC,0
(Enum algo first, int pps_selector last)
> +}
> EXPORT_SYMBOL(mipi_dsi_compression_mode);
>
> /**
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 3011d33eccbd..78cb7b688b1d 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
> return -EINVAL;
> }
>
> +enum mipi_dsi_compression_algo {
> + MIPI_DSI_COMPRESSION_DSC = 0,
Add 1.1? Or does it also allow 1.2 (when the version is also set via PPS)?
> + MIPI_DSI_COMPRESSION_VENDOR = 3,
> + /* other two values are reserved, DSI 1.3 */
> +};
> +
> struct mipi_dsi_device *
> mipi_dsi_device_register_full(struct mipi_dsi_host *host,
> const struct mipi_dsi_device_info *info);
> @@ -242,6 +248,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
> int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> u16 value);
> int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
> +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> + unsigned int pps_selector,
> + enum mipi_dsi_compression_algo algo);
Oh, this declaration is inverse from the definition...
- Marijn
> int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> const struct drm_dsc_picture_parameter_set *pps);
>
>
> --
> 2.39.2
>
On 2024-04-02 02:51:15, Dmitry Baryshkov wrote:
> From: Sumit Semwal <[email protected]>
>
> LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
> phones.
@60Hz?
>
> Signed-off-by: Sumit Semwal <[email protected]>
> [vinod: Add DSC support]
> Signed-off-by: Vinod Koul <[email protected]>
> [caleb: cleanup and support turning off the panel]
> Signed-off-by: Caleb Connolly <[email protected]>
> [DB: partially rewrote the driver and fixed DSC programming]
> Signed-off-by: Dmitry Baryshkov <[email protected]>
Some small nits but I think this deserves a:
Reviewed-by: Marijn Suijten <[email protected]>
> ---
> MAINTAINERS | 8 +
> drivers/gpu/drm/panel/Kconfig | 11 ++
> drivers/gpu/drm/panel/Makefile | 1 +
> drivers/gpu/drm/panel/panel-lg-sw43408.c | 326 +++++++++++++++++++++++++++++++
> 4 files changed, 346 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d36c19c1bf81..4cc43c16e07e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6789,6 +6789,14 @@ S: Maintained
> F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
> F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
>
> +DRM DRIVER FOR LG SW43408 PANELS
> +M: Sumit Semwal <[email protected]>
> +M: Caleb Connolly <[email protected]>
> +S: Maintained
> +T: git git://anongit.freedesktop.org/drm/drm-misc
> +F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
> +F: drivers/gpu/drm/panel/panel-lg-sw43408.c
> +
> DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
> M: Paul Kocialkowski <[email protected]>
> S: Supported
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 6dc451f58a3e..a55e9437c8cf 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
> Say Y here if you want to enable support for LG4573 RGB panel.
> To compile this driver as a module, choose M here.
>
> +config DRM_PANEL_LG_SW43408
> + tristate "LG SW43408 panel"
> + depends on OF
> + depends on DRM_MIPI_DSI
> + depends on BACKLIGHT_CLASS_DEVICE
> + help
> + Say Y here if you want to enable support for LG sw43408 panel.
> + The panel has a 1080x2160 resolution and uses
> + 24 bit RGB per pixel. It provides a MIPI DSI interface to
> + the host and has a built-in LED backlight.
> +
> config DRM_PANEL_MAGNACHIP_D53E6EA8966
> tristate "Magnachip D53E6EA8966 DSI panel"
> depends on OF && SPI
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 24a02655d726..0b40b010e8e7 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
> obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
> 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_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
> obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
> diff --git a/drivers/gpu/drm/panel/panel-lg-sw43408.c b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> new file mode 100644
> index 000000000000..c7611bfa796b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> @@ -0,0 +1,326 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019-2024 Linaro Ltd
> + * Author: Sumit Semwal <[email protected]>
> + * Dmitry Baryshkov <[email protected]>
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/mipi_display.h>
> +
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/display/drm_dsc.h>
> +#include <drm/display/drm_dsc_helper.h>
> +
> +#define NUM_SUPPLIES 2
> +
> +struct sw43408_panel {
> + struct drm_panel base;
> + struct mipi_dsi_device *link;
> +
> + const struct drm_display_mode *mode;
> +
> + struct regulator_bulk_data supplies[NUM_SUPPLIES];
> +
> + struct gpio_desc *reset_gpio;
> +
> + struct drm_dsc_config dsc;
> +};
> +
> +static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
> +{
> + return container_of(panel, struct sw43408_panel, base);
> +}
> +
> +static int sw43408_unprepare(struct drm_panel *panel)
> +{
> + struct sw43408_panel *ctx = to_panel_info(panel);
> + int ret;
> +
> + ret = mipi_dsi_dcs_set_display_off(ctx->link);
> + if (ret < 0)
> + dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
> +
> + ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
> + if (ret < 0)
> + dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
> +
> + msleep(100);
> +
> + gpiod_set_value(ctx->reset_gpio, 1);
> +
> + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> +}
> +
> +static int sw43408_program(struct drm_panel *panel)
> +{
> + struct sw43408_panel *ctx = to_panel_info(panel);
> + struct drm_dsc_picture_parameter_set pps;
> +
> + mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
> +
> + mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +
> + mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
> + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
> +
> + mipi_dsi_dcs_exit_sleep_mode(ctx->link);
> +
> + msleep(135);
> +
> + /* COMPRESSION_MODE moved after setting the PPS */
> +
> + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
> + 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
> + 0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
> + 0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
> + 0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
> + 0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
> + 0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
> + 0x01);
> + msleep(85);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
> + 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
> + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
> + 0x16, 0x16);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
> + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
> + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
> +
> + mipi_dsi_dcs_set_display_on(ctx->link);
> +
> + msleep(50);
> +
> + ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
> + mipi_dsi_picture_parameter_set(ctx->link, &pps);
> +
> + /* This panel uses shifted PPS selectors:
> + * 1 if pps_identifier is 0
> + * 2 if pps_identifier is 1
> + */
> + mipi_dsi_compression_mode_ext(ctx->link, true,
> + MIPI_DSI_COMPRESSION_DSC, 1);
Let's be careful to watch the order of parameters here whichever way you fix it
up in the patch that introduces this function.
> +
> + ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + return 0;
> +}
> +
> +static int sw43408_prepare(struct drm_panel *panel)
> +{
> + struct sw43408_panel *ctx = to_panel_info(panel);
> + int ret;
> +
> + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> + if (ret < 0)
> + return ret;
> +
> + usleep_range(5000, 6000);
> +
> + gpiod_set_value(ctx->reset_gpio, 0);
> + usleep_range(9000, 10000);
> + gpiod_set_value(ctx->reset_gpio, 1);
> + usleep_range(1000, 2000);
> + gpiod_set_value(ctx->reset_gpio, 0);
> + usleep_range(9000, 10000);
> +
> + ret = sw43408_program(panel);
> + if (ret)
> + goto poweroff;
> +
> + return 0;
> +
> +poweroff:
> + gpiod_set_value(ctx->reset_gpio, 1);
> + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> + return ret;
> +}
> +
> +static int sw43408_get_modes(struct drm_panel *panel,
> + struct drm_connector *connector)
> +{
> + struct sw43408_panel *ctx = to_panel_info(panel);
> +
> + return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
> +}
> +
> +static int sw43408_backlight_update_status(struct backlight_device *bl)
> +{
> + struct mipi_dsi_device *dsi = bl_get_data(bl);
> + uint16_t brightness = backlight_get_brightness(bl);
> +
> + return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> +}
> +
> +const struct backlight_ops sw43408_backlight_ops = {
> + .update_status = sw43408_backlight_update_status,
> +};
> +
> +static int sw43408_backlight_init(struct sw43408_panel *ctx)
> +{
> + struct device *dev = &ctx->link->dev;
> + const struct backlight_properties props = {
> + .type = BACKLIGHT_PLATFORM,
> + .brightness = 255,
> + .max_brightness = 255,
> + };
> +
> + ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
> + ctx->link,
> + &sw43408_backlight_ops,
> + &props);
> +
> + if (IS_ERR(ctx->base.backlight))
> + return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
> + "Failed to create backlight\n");
> +
> + return 0;
> +}
> +
> +static const struct drm_panel_funcs sw43408_funcs = {
> + .unprepare = sw43408_unprepare,
> + .prepare = sw43408_prepare,
> + .get_modes = sw43408_get_modes,
> +};
> +
> +static const struct drm_display_mode sw43408_default_mode = {
> + .clock = 152340,
Since this value is calculated from the values below, I prefer to just show the
origin of the value:
.clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
> +
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 20,
> + .hsync_end = 1080 + 20 + 32,
> + .htotal = 1080 + 20 + 32 + 20,
> +
> + .vdisplay = 2160,
> + .vsync_start = 2160 + 20,
> + .vsync_end = 2160 + 20 + 4,
> + .vtotal = 2160 + 20 + 4 + 20,
> +
> + .width_mm = 62,
> + .height_mm = 124,
> +
> + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> +};
> +
> +static const struct of_device_id sw43408_of_match[] = {
> + { .compatible = "lg,sw43408", .data = &sw43408_default_mode },
Will you ever use multiple compatibles to select different modes?
For panels that support multiple modes (e.g. a lot of high-end sony devices
with their 4k@120Hz screens) I'm still planning on adding an atomic_prepare() to
drm_bridge and drm_panel to make it possible to program the DSC block and send
DCS relative to the selected mode (and/or perform a fluent mode switch).
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sw43408_of_match);
> +
> +static int sw43408_add(struct sw43408_panel *ctx)
> +{
> + struct device *dev = &ctx->link->dev;
> + int ret;
> +
> + ctx->supplies[0].supply = "vddi"; /* 1.88 V */
> + ctx->supplies[0].init_load_uA = 62000;
> + ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
> + ctx->supplies[1].init_load_uA = 857000;
> +
> + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
> + ctx->supplies);
> + if (ret < 0)
> + return ret;
> +
> + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(ctx->reset_gpio)) {
> + dev_err(dev, "cannot get reset gpio %ld\n",
> + PTR_ERR(ctx->reset_gpio));
> + return PTR_ERR(ctx->reset_gpio);
> + }
> +
> + ret = sw43408_backlight_init(ctx);
> + if (ret < 0)
> + return ret;
> +
> + ctx->base.prepare_prev_first = true;
> +
> + drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
> +
> + drm_panel_add(&ctx->base);
> + return ret;
> +}
> +
> +static int sw43408_probe(struct mipi_dsi_device *dsi)
> +{
> + struct sw43408_panel *ctx;
> + int ret;
> +
> + ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->mode = of_device_get_match_data(&dsi->dev);
> + dsi->mode_flags = MIPI_DSI_MODE_LPM;
> + dsi->format = MIPI_DSI_FMT_RGB888;
> + dsi->lanes = 4;
> +
> + ctx->link = dsi;
> + mipi_dsi_set_drvdata(dsi, ctx);
> +
> + ret = sw43408_add(ctx);
> + if (ret < 0)
> + return ret;
> +
> + /* The panel is DSC panel only, set the dsc params */
Grammar?
> + ctx->dsc.dsc_version_major = 0x1;
> + ctx->dsc.dsc_version_minor = 0x1;
> +
> + /* slice_count * slice_width == width */
> + ctx->dsc.slice_height = 16;
> + ctx->dsc.slice_width = 540;
> + ctx->dsc.slice_count = 2;
> + ctx->dsc.bits_per_component = 8;
> + ctx->dsc.bits_per_pixel = 8 << 4;
> + ctx->dsc.block_pred_enable = true;
> +
> + dsi->dsc = &ctx->dsc;
> +
> + return mipi_dsi_attach(dsi);
> +}
> +
> +static void sw43408_remove(struct mipi_dsi_device *dsi)
> +{
> + struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
> + int ret;
> +
> + ret = sw43408_unprepare(&ctx->base);
> + if (ret < 0)
> + dev_err(&dsi->dev, "failed to unprepare panel: %d\n",
> + ret);
> +
> + ret = mipi_dsi_detach(dsi);
> + if (ret < 0)
> + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
> +
> + drm_panel_remove(&ctx->base);
> +}
> +
> +static struct mipi_dsi_driver sw43408_driver = {
> + .driver = {
> + .name = "panel-lg-sw43408",
> + .of_match_table = sw43408_of_match,
> + },
> + .probe = sw43408_probe,
> + .remove = sw43408_remove,
> +};
> +module_mipi_dsi_driver(sw43408_driver);
> +
> +MODULE_AUTHOR("Sumit Semwal <[email protected]>");
> +MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
> +MODULE_LICENSE("GPL");
>
> --
> 2.39.2
>
On 2024-04-02 10:23:22, Dmitry Baryshkov wrote:
> On Tue, 2 Apr 2024 at 09:31, Krzysztof Kozlowski
> <[email protected]> wrote:
> >
> > On 02/04/2024 01:51, Dmitry Baryshkov wrote:
> > > From: Sumit Semwal <[email protected]>
> > >
> > > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
> > > phones.
> > >
> > > Signed-off-by: Vinod Koul <[email protected]>
> > > Signed-off-by: Sumit Semwal <[email protected]>
> > > [caleb: convert to yaml]
> > > Signed-off-by: Caleb Connolly <[email protected]>
> > > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > > ---
> >
> > Tags missing.
> >
> > `b4 trailers -u`
>
> Excuse me, I keep on forgetting it.
Does a similar thing exist for adding Cc: tags for all reviewers/replyers to an
earlier version, even if said reviewer didn't yet provide R-b/A-b or other tags?
I'd like to have the next revisions in my inbox as well after leaving
comments :)
Thanks! - Marijn
On 2024-04-02 02:51:13, Dmitry Baryshkov wrote:
> The functions mipi_dsi_compression_mode() and
> mipi_dsi_picture_parameter_set() return 0-or-error rather than a buffer
> size. Follow example of other similar MIPI DSI functions and use int
> return type instead of size_t.
>
> Fixes: f4dea1aaa9a1 ("drm/dsi: add helpers for DSI compression mode and PPS packets")
> Signed-off-by: Dmitry Baryshkov <[email protected]>
Reviewed-by: Marijn Suijten <[email protected]>
> ---
> drivers/gpu/drm/drm_mipi_dsi.c | 6 +++---
> include/drm/drm_mipi_dsi.h | 6 +++---
> 2 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index ef6e416522f8..9874ff6d4718 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -654,7 +654,7 @@ EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
> *
> * Return: 0 on success or a negative error code on failure.
> */
> -ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> {
> /* Note: Needs updating for non-default PPS or algorithm */
> u8 tx[2] = { enable << 0, 0 };
> @@ -679,8 +679,8 @@ EXPORT_SYMBOL(mipi_dsi_compression_mode);
> *
> * Return: 0 on success or a negative error code on failure.
> */
> -ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> - const struct drm_dsc_picture_parameter_set *pps)
> +int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> + const struct drm_dsc_picture_parameter_set *pps)
> {
> struct mipi_dsi_msg msg = {
> .channel = dsi->channel,
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index c0aec0d4d664..3011d33eccbd 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -241,9 +241,9 @@ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
> int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
> int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> u16 value);
> -ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
> -ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> - const struct drm_dsc_picture_parameter_set *pps);
> +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
> +int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> + const struct drm_dsc_picture_parameter_set *pps);
>
> ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
> size_t size);
>
> --
> 2.39.2
>
On Tue, Apr 02, 2024 at 10:59:11PM +0200, Marijn Suijten wrote:
> On 2024-04-02 10:23:22, Dmitry Baryshkov wrote:
> > On Tue, 2 Apr 2024 at 09:31, Krzysztof Kozlowski
> > <[email protected]> wrote:
> > >
> > > On 02/04/2024 01:51, Dmitry Baryshkov wrote:
> > > > From: Sumit Semwal <[email protected]>
> > > >
> > > > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
> > > > phones.
> > > >
> > > > Signed-off-by: Vinod Koul <[email protected]>
> > > > Signed-off-by: Sumit Semwal <[email protected]>
> > > > [caleb: convert to yaml]
> > > > Signed-off-by: Caleb Connolly <[email protected]>
> > > > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > > > ---
> > >
> > > Tags missing.
> > >
> > > `b4 trailers -u`
> >
> > Excuse me, I keep on forgetting it.
>
> Does a similar thing exist for adding Cc: tags for all reviewers/replyers to an
> earlier version, even if said reviewer didn't yet provide R-b/A-b or other tags?
>
> I'd like to have the next revisions in my inbox as well after leaving
> comments :)
Unfortunately I don't know such option.
>
> Thanks! - Marijn
--
With best wishes
Dmitry
On Tue, Apr 02, 2024 at 11:09:29PM +0200, Marijn Suijten wrote:
> On 2024-04-02 02:51:14, Dmitry Baryshkov wrote:
> > Add the extended version of mipi_dsi_compression_mode(). It provides
> > a way to specify the algorithm and PPS selector.
> >
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > drivers/gpu/drm/drm_mipi_dsi.c | 33 +++++++++++++++++++++++++++------
> > include/drm/drm_mipi_dsi.h | 9 +++++++++
> > 2 files changed, 36 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> > index 9874ff6d4718..0ecbc811eb7a 100644
> > --- a/drivers/gpu/drm/drm_mipi_dsi.c
> > +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> > @@ -645,19 +645,24 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
> >
> > /**
> > - * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> > + * mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
> > * @dsi: DSI peripheral device
> > * @enable: Whether to enable or disable the DSC
> > + * @algo: Selected algorithm
> > + * @pps_selector: The PPS selector
>
> Not a big fan of paraphrasing the parameter name, it adds no value. How about
> describing what this parameter means and what it does?:
>
> PPS table index to use. Corresponds to a table pre-programmed on the peripheral
> or a table programmed with &drm_dsc_picture_parameter_set.pps_identifier.
>
> (That should be a valid kernel-doc cross-reference to the field)
I don't think such meaning is defined in the standard. In the end, the
pps_identifier is 8-bit wide, while this field has just 2 bits and 4
possible values. The standards are pretty vague about this field.
>
> > *
> > - * Enable or disable Display Stream Compression on the peripheral using the
> > - * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> > + * Enable or disable Display Stream Compression on the peripheral.
> > *
> > * Return: 0 on success or a negative error code on failure.
> > */
> > -int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> > +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> > + enum mipi_dsi_compression_algo algo,
> > + unsigned int pps_selector)
> > {
> > - /* Note: Needs updating for non-default PPS or algorithm */
> > - u8 tx[2] = { enable << 0, 0 };
> > + u8 data = (enable << 0) |
> > + (algo << 1) |
> > + (pps_selector << 4);
>
> Do we need some size validation (if > 3 return -EINVAL)? FIELD_PREP() might be
> too heavy though.
Ack, let's add it.
>
> > + u8 tx[2] = { data, 0 };
> > struct mipi_dsi_msg msg = {
> > .channel = dsi->channel,
> > .type = MIPI_DSI_COMPRESSION_MODE,
> > @@ -668,6 +673,22 @@ int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> >
> > return (ret < 0) ? ret : 0;
> > }
> > +EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
> > +
> > +/**
> > + * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> > + * @dsi: DSI peripheral device
> > + * @enable: Whether to enable or disable the DSC
> > + *
> > + * Enable or disable Display Stream Compression on the peripheral using the
> > + * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> > + *
> > + * Return: 0 on success or a negative error code on failure.
> > + */
> > +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> > +{
> > + return mipi_dsi_compression_mode_ext(dsi, enable, 0, MIPI_DSI_COMPRESSION_DSC);
>
> I hope the compiler complains here that it should be MIPI_DSI_COMPRESSION_DSC,0
Nope, it didn't. Both are integers.
>
> (Enum algo first, int pps_selector last)
>
> > +}
> > EXPORT_SYMBOL(mipi_dsi_compression_mode);
> >
> > /**
> > diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> > index 3011d33eccbd..78cb7b688b1d 100644
> > --- a/include/drm/drm_mipi_dsi.h
> > +++ b/include/drm/drm_mipi_dsi.h
> > @@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
> > return -EINVAL;
> > }
> >
> > +enum mipi_dsi_compression_algo {
> > + MIPI_DSI_COMPRESSION_DSC = 0,
>
> Add 1.1? Or does it also allow 1.2 (when the version is also set via PPS)?
I have only DSI 1.3 at hand, which only talks about 1.1. I think 1.2 is
allowed by inheritance. That's why I skipped the version here.
>
> > + MIPI_DSI_COMPRESSION_VENDOR = 3,
> > + /* other two values are reserved, DSI 1.3 */
> > +};
> > +
> > struct mipi_dsi_device *
> > mipi_dsi_device_register_full(struct mipi_dsi_host *host,
> > const struct mipi_dsi_device_info *info);
> > @@ -242,6 +248,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
> > int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > u16 value);
> > int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
> > +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> > + unsigned int pps_selector,
> > + enum mipi_dsi_compression_algo algo);
>
> Oh, this declaration is inverse from the definition...
I'll check it.
>
> - Marijn
>
> > int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> > const struct drm_dsc_picture_parameter_set *pps);
> >
> >
> > --
> > 2.39.2
> >
--
With best wishes
Dmitry
On Tue, Apr 02, 2024 at 11:17:52PM +0200, Marijn Suijten wrote:
> On 2024-04-02 02:51:15, Dmitry Baryshkov wrote:
> > From: Sumit Semwal <[email protected]>
> >
> > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
> > phones.
>
> @60Hz?
With the current settings and timings I'm only getting 30 Hz. I have to
double the mode->clock to get 60.
>
> >
> > Signed-off-by: Sumit Semwal <[email protected]>
> > [vinod: Add DSC support]
> > Signed-off-by: Vinod Koul <[email protected]>
> > [caleb: cleanup and support turning off the panel]
> > Signed-off-by: Caleb Connolly <[email protected]>
> > [DB: partially rewrote the driver and fixed DSC programming]
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
>
> Some small nits but I think this deserves a:
>
> Reviewed-by: Marijn Suijten <[email protected]>
>
> > ---
> > MAINTAINERS | 8 +
> > drivers/gpu/drm/panel/Kconfig | 11 ++
> > drivers/gpu/drm/panel/Makefile | 1 +
> > drivers/gpu/drm/panel/panel-lg-sw43408.c | 326 +++++++++++++++++++++++++++++++
> > 4 files changed, 346 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index d36c19c1bf81..4cc43c16e07e 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6789,6 +6789,14 @@ S: Maintained
> > F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
> > F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
> >
> > +DRM DRIVER FOR LG SW43408 PANELS
> > +M: Sumit Semwal <[email protected]>
> > +M: Caleb Connolly <[email protected]>
> > +S: Maintained
> > +T: git git://anongit.freedesktop.org/drm/drm-misc
> > +F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
> > +F: drivers/gpu/drm/panel/panel-lg-sw43408.c
> > +
> > DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
> > M: Paul Kocialkowski <[email protected]>
> > S: Supported
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index 6dc451f58a3e..a55e9437c8cf 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
> > Say Y here if you want to enable support for LG4573 RGB panel.
> > To compile this driver as a module, choose M here.
> >
> > +config DRM_PANEL_LG_SW43408
> > + tristate "LG SW43408 panel"
> > + depends on OF
> > + depends on DRM_MIPI_DSI
> > + depends on BACKLIGHT_CLASS_DEVICE
> > + help
> > + Say Y here if you want to enable support for LG sw43408 panel.
> > + The panel has a 1080x2160 resolution and uses
> > + 24 bit RGB per pixel. It provides a MIPI DSI interface to
> > + the host and has a built-in LED backlight.
> > +
> > config DRM_PANEL_MAGNACHIP_D53E6EA8966
> > tristate "Magnachip D53E6EA8966 DSI panel"
> > depends on OF && SPI
> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > index 24a02655d726..0b40b010e8e7 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
> > obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
> > 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_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
> > obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
> > diff --git a/drivers/gpu/drm/panel/panel-lg-sw43408.c b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> > new file mode 100644
> > index 000000000000..c7611bfa796b
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> > @@ -0,0 +1,326 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2019-2024 Linaro Ltd
> > + * Author: Sumit Semwal <[email protected]>
> > + * Dmitry Baryshkov <[email protected]>
> > + */
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#include <video/mipi_display.h>
> > +
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/display/drm_dsc.h>
> > +#include <drm/display/drm_dsc_helper.h>
> > +
> > +#define NUM_SUPPLIES 2
> > +
> > +struct sw43408_panel {
> > + struct drm_panel base;
> > + struct mipi_dsi_device *link;
> > +
> > + const struct drm_display_mode *mode;
> > +
> > + struct regulator_bulk_data supplies[NUM_SUPPLIES];
> > +
> > + struct gpio_desc *reset_gpio;
> > +
> > + struct drm_dsc_config dsc;
> > +};
> > +
> > +static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
> > +{
> > + return container_of(panel, struct sw43408_panel, base);
> > +}
> > +
> > +static int sw43408_unprepare(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + int ret;
> > +
> > + ret = mipi_dsi_dcs_set_display_off(ctx->link);
> > + if (ret < 0)
> > + dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
> > +
> > + ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
> > + if (ret < 0)
> > + dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
> > +
> > + msleep(100);
> > +
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > +
> > + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > +}
> > +
> > +static int sw43408_program(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + struct drm_dsc_picture_parameter_set pps;
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
> > +
> > + mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
> > +
> > + mipi_dsi_dcs_exit_sleep_mode(ctx->link);
> > +
> > + msleep(135);
> > +
> > + /* COMPRESSION_MODE moved after setting the PPS */
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
> > + 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
> > + 0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
> > + 0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
> > + 0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
> > + 0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
> > + 0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
> > + 0x01);
> > + msleep(85);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
> > + 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
> > + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
> > + 0x16, 0x16);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
> > +
> > + mipi_dsi_dcs_set_display_on(ctx->link);
> > +
> > + msleep(50);
> > +
> > + ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > + drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
> > + mipi_dsi_picture_parameter_set(ctx->link, &pps);
> > +
> > + /* This panel uses shifted PPS selectors:
> > + * 1 if pps_identifier is 0
> > + * 2 if pps_identifier is 1
> > + */
> > + mipi_dsi_compression_mode_ext(ctx->link, true,
> > + MIPI_DSI_COMPRESSION_DSC, 1);
>
> Let's be careful to watch the order of parameters here whichever way you fix it
> up in the patch that introduces this function.
>
> > +
> > + ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > + return 0;
> > +}
> > +
> > +static int sw43408_prepare(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + int ret;
> > +
> > + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + if (ret < 0)
> > + return ret;
> > +
> > + usleep_range(5000, 6000);
> > +
> > + gpiod_set_value(ctx->reset_gpio, 0);
> > + usleep_range(9000, 10000);
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > + usleep_range(1000, 2000);
> > + gpiod_set_value(ctx->reset_gpio, 0);
> > + usleep_range(9000, 10000);
> > +
> > + ret = sw43408_program(panel);
> > + if (ret)
> > + goto poweroff;
> > +
> > + return 0;
> > +
> > +poweroff:
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + return ret;
> > +}
> > +
> > +static int sw43408_get_modes(struct drm_panel *panel,
> > + struct drm_connector *connector)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > +
> > + return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
> > +}
> > +
> > +static int sw43408_backlight_update_status(struct backlight_device *bl)
> > +{
> > + struct mipi_dsi_device *dsi = bl_get_data(bl);
> > + uint16_t brightness = backlight_get_brightness(bl);
> > +
> > + return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> > +}
> > +
> > +const struct backlight_ops sw43408_backlight_ops = {
> > + .update_status = sw43408_backlight_update_status,
> > +};
> > +
> > +static int sw43408_backlight_init(struct sw43408_panel *ctx)
> > +{
> > + struct device *dev = &ctx->link->dev;
> > + const struct backlight_properties props = {
> > + .type = BACKLIGHT_PLATFORM,
> > + .brightness = 255,
> > + .max_brightness = 255,
> > + };
> > +
> > + ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
> > + ctx->link,
> > + &sw43408_backlight_ops,
> > + &props);
> > +
> > + if (IS_ERR(ctx->base.backlight))
> > + return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
> > + "Failed to create backlight\n");
> > +
> > + return 0;
> > +}
> > +
> > +static const struct drm_panel_funcs sw43408_funcs = {
> > + .unprepare = sw43408_unprepare,
> > + .prepare = sw43408_prepare,
> > + .get_modes = sw43408_get_modes,
> > +};
> > +
> > +static const struct drm_display_mode sw43408_default_mode = {
> > + .clock = 152340,
>
> Since this value is calculated from the values below, I prefer to just show the
> origin of the value:
>
> .clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
Sure
>
> > +
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 20,
> > + .hsync_end = 1080 + 20 + 32,
> > + .htotal = 1080 + 20 + 32 + 20,
> > +
> > + .vdisplay = 2160,
> > + .vsync_start = 2160 + 20,
> > + .vsync_end = 2160 + 20 + 4,
> > + .vtotal = 2160 + 20 + 4 + 20,
> > +
> > + .width_mm = 62,
> > + .height_mm = 124,
> > +
> > + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> > +};
> > +
> > +static const struct of_device_id sw43408_of_match[] = {
> > + { .compatible = "lg,sw43408", .data = &sw43408_default_mode },
>
> Will you ever use multiple compatibles to select different modes?
>
> For panels that support multiple modes (e.g. a lot of high-end sony devices
> with their 4k@120Hz screens) I'm still planning on adding an atomic_prepare() to
> drm_bridge and drm_panel to make it possible to program the DSC block and send
> DCS relative to the selected mode (and/or perform a fluent mode switch).
I think this got inherited from the initial implementation by Sumit.
Let's rewrite this too.
>
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, sw43408_of_match);
> > +
> > +static int sw43408_add(struct sw43408_panel *ctx)
> > +{
> > + struct device *dev = &ctx->link->dev;
> > + int ret;
> > +
> > + ctx->supplies[0].supply = "vddi"; /* 1.88 V */
> > + ctx->supplies[0].init_load_uA = 62000;
> > + ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
> > + ctx->supplies[1].init_load_uA = 857000;
> > +
> > + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
> > + ctx->supplies);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > + if (IS_ERR(ctx->reset_gpio)) {
> > + dev_err(dev, "cannot get reset gpio %ld\n",
> > + PTR_ERR(ctx->reset_gpio));
> > + return PTR_ERR(ctx->reset_gpio);
> > + }
> > +
> > + ret = sw43408_backlight_init(ctx);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ctx->base.prepare_prev_first = true;
> > +
> > + drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
> > +
> > + drm_panel_add(&ctx->base);
> > + return ret;
> > +}
> > +
> > +static int sw43408_probe(struct mipi_dsi_device *dsi)
> > +{
> > + struct sw43408_panel *ctx;
> > + int ret;
> > +
> > + ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > +
> > + ctx->mode = of_device_get_match_data(&dsi->dev);
> > + dsi->mode_flags = MIPI_DSI_MODE_LPM;
> > + dsi->format = MIPI_DSI_FMT_RGB888;
> > + dsi->lanes = 4;
> > +
> > + ctx->link = dsi;
> > + mipi_dsi_set_drvdata(dsi, ctx);
> > +
> > + ret = sw43408_add(ctx);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* The panel is DSC panel only, set the dsc params */
>
> Grammar?
>
> > + ctx->dsc.dsc_version_major = 0x1;
> > + ctx->dsc.dsc_version_minor = 0x1;
> > +
> > + /* slice_count * slice_width == width */
> > + ctx->dsc.slice_height = 16;
> > + ctx->dsc.slice_width = 540;
> > + ctx->dsc.slice_count = 2;
> > + ctx->dsc.bits_per_component = 8;
> > + ctx->dsc.bits_per_pixel = 8 << 4;
> > + ctx->dsc.block_pred_enable = true;
> > +
> > + dsi->dsc = &ctx->dsc;
> > +
> > + return mipi_dsi_attach(dsi);
> > +}
> > +
> > +static void sw43408_remove(struct mipi_dsi_device *dsi)
> > +{
> > + struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
> > + int ret;
> > +
> > + ret = sw43408_unprepare(&ctx->base);
> > + if (ret < 0)
> > + dev_err(&dsi->dev, "failed to unprepare panel: %d\n",
> > + ret);
> > +
> > + ret = mipi_dsi_detach(dsi);
> > + if (ret < 0)
> > + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
> > +
> > + drm_panel_remove(&ctx->base);
> > +}
> > +
> > +static struct mipi_dsi_driver sw43408_driver = {
> > + .driver = {
> > + .name = "panel-lg-sw43408",
> > + .of_match_table = sw43408_of_match,
> > + },
> > + .probe = sw43408_probe,
> > + .remove = sw43408_remove,
> > +};
> > +module_mipi_dsi_driver(sw43408_driver);
> > +
> > +MODULE_AUTHOR("Sumit Semwal <[email protected]>");
> > +MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
> > +MODULE_LICENSE("GPL");
> >
> > --
> > 2.39.2
> >
--
With best wishes
Dmitry
On 2024-04-03 05:37:29, Dmitry Baryshkov wrote:
> On Tue, Apr 02, 2024 at 11:17:52PM +0200, Marijn Suijten wrote:
> > On 2024-04-02 02:51:15, Dmitry Baryshkov wrote:
> > > From: Sumit Semwal <[email protected]>
> > >
> > > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
> > > phones.
> >
> > @60Hz?
>
> With the current settings and timings I'm only getting 30 Hz. I have to
> double the mode->clock to get 60.
Still seems useful to mention (here and in Kconfig). The proposed driver emits
a mode to userspace of 60Hz, maybe the commit message should say that in the
current state "something" prevents it from going that fast?
Since I keep forgetting (because it's not mentioned anywhere) that this is a
cmdmode panel (or at least configured for that with the current driver), I'd
again suggest to play with sync_cfg_height. If setting it to 0xfff0 results in
timeouts, your tear GPIO is misconfigured and not making the MDP aware of the
actual tick rate.
Otherwise, more likely, just bump up the porches a bit, based on the discussions
around reduce_pclk_for_compression() /not/ accounting for transfer time in
cmdmode. In one of my drivers (pending eternal cleanup hell) I inlined the
calculation to reverse what the "right" porch should be based on a downstream
clock rate:
https://github.com/somainline/linux/commit/85978a69cde088a23963c03758dad5f1a2e79bab#diff-a9ac8689e45c59a4fe9aa150e4bd53675687f5c8b4aecb40b5b5b66b864257e0R353-R366
And separately, though I cannot find it, there have been (more accurate?)
calculations based on downstream `qcom,mdss-dsi-panel-jitter` and friends.
- Marijn