All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add DSI panel driver for Raydium RM67191
@ 2019-06-14 11:51 Robert Chiras
  2019-06-14 11:51 ` [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
  2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  0 siblings, 2 replies; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 11:51 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland
  Cc: dri-devel, devicetree, linux-kernel, linux-imx, Robert Chiras

This patch-set contains the DRM panel driver and dt-bindings documentation
for the DSI driven panel: Raydium RM67191.

Robert Chiras (2):
  dt-bindings: display: panel: Add support for Raydium RM67191 panel
  drm/panel: Add support for Raydium RM67191 panel driver

 .../bindings/display/panel/raydium,rm67191.txt     |  42 ++
 drivers/gpu/drm/panel/Kconfig                      |   9 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-raydium-rm67191.c      | 730 +++++++++++++++++++++
 4 files changed, 782 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
 create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-14 11:51 [PATCH 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
@ 2019-06-14 11:51 ` Robert Chiras
  2019-06-14 12:59     ` Fabio Estevam
  2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  1 sibling, 1 reply; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 11:51 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland
  Cc: dri-devel, devicetree, linux-kernel, linux-imx, Robert Chiras

Add dt-bindings documentation for Raydium RM67191 DSI panel.

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
 .../bindings/display/panel/raydium,rm67191.txt     | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt

diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
new file mode 100644
index 0000000..5a6268d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
@@ -0,0 +1,42 @@
+Raydium RM67171 OLED LCD panel with MIPI-DSI protocol
+
+Required properties:
+- compatible: 		"raydium,rm67191"
+- reg:			virtual channel for MIPI-DSI protocol
+			must be <0>
+- dsi-lanes:		number of DSI lanes to be used
+			must be <3> or <4>
+- port: 		input port node with endpoint definition as
+			defined in Documentation/devicetree/bindings/graph.txt;
+			the input port should be connected to a MIPI-DSI device
+			driver
+
+Optional properties:
+- reset-gpio:		a GPIO spec for the RST_B GPIO pin
+- pinctrl-0		phandle to the pin settings for the reset pin
+- width-mm:		physical panel width [mm]
+- height-mm:		physical panel height [mm]
+- display-timings:	timings for the connected panel according to [1]
+- video-mode:		0 - burst-mode
+			1 - non-burst with sync event
+			2 - non-burst with sync pulse
+
+[1]: Documentation/devicetree/bindings/display/display-timing.txt
+
+Example:
+
+	panel@0 {
+		compatible = "raydium,rm67191";
+		reg = <0>;
+		pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;
+		reset-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+		dsi-lanes = <4>;
+		width-mm = <68>;
+		height-mm = <121>;
+
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&mipi_out>;
+			};
+		};
+	};
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 11:51 [PATCH 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
  2019-06-14 11:51 ` [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
@ 2019-06-14 11:51 ` Robert Chiras
  2019-06-14 12:03   ` Daniel Baluta
                     ` (2 more replies)
  1 sibling, 3 replies; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 11:51 UTC (permalink / raw)
  To: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland
  Cc: dri-devel, devicetree, linux-kernel, linux-imx, Robert Chiras

This patch adds Raydium RM67191 TFT LCD panel driver (MIPI-DSI
protocol).

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
---
 drivers/gpu/drm/panel/Kconfig                 |   9 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 drivers/gpu/drm/panel/panel-raydium-rm67191.c | 730 ++++++++++++++++++++++++++
 3 files changed, 740 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d9d931a..8be1ac1 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -159,6 +159,15 @@ config DRM_PANEL_RASPBERRYPI_TOUCHSCREEN
 	  Pi 7" Touchscreen.  To compile this driver as a module,
 	  choose M here.
 
+config DRM_PANEL_RAYDIUM_RM67191
+	tristate "Raydium RM67191 FHD 1080x1920 DSI video mode 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 Raydium RM67191 FHD
+	  (1080x1920) DSI panel.
+
 config DRM_PANEL_RAYDIUM_RM68200
 	tristate "Raydium RM68200 720x1280 DSI video mode panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index fb0cb3a..1fc0f68 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
 obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
+obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o
 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
 obj-$(CONFIG_DRM_PANEL_ROCKTECH_JH057N00900) += panel-rocktech-jh057n00900.o
 obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
new file mode 100644
index 0000000..75bfb03
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX drm driver - Raydium MIPI-DSI panel driver
+ *
+ * Copyright (C) 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.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 <video/of_videomode.h>
+#include <video/videomode.h>
+
+/* Write Manufacture Command Set Control */
+#define WRMAUCCTR 0xFE
+
+/* Manufacturer Command Set pages (CMD2) */
+struct cmd_set_entry {
+	u8 cmd;
+	u8 param;
+};
+
+/*
+ * There is no description in the Reference Manual about these commands.
+ * We received them from vendor, so just use them as is.
+ */
+static const struct cmd_set_entry manufacturer_cmd_set[] = {
+	{0xFE, 0x0B},
+	{0x28, 0x40},
+	{0x29, 0x4F},
+	{0xFE, 0x0E},
+	{0x4B, 0x00},
+	{0x4C, 0x0F},
+	{0x4D, 0x20},
+	{0x4E, 0x40},
+	{0x4F, 0x60},
+	{0x50, 0xA0},
+	{0x51, 0xC0},
+	{0x52, 0xE0},
+	{0x53, 0xFF},
+	{0xFE, 0x0D},
+	{0x18, 0x08},
+	{0x42, 0x00},
+	{0x08, 0x41},
+	{0x46, 0x02},
+	{0x72, 0x09},
+	{0xFE, 0x0A},
+	{0x24, 0x17},
+	{0x04, 0x07},
+	{0x1A, 0x0C},
+	{0x0F, 0x44},
+	{0xFE, 0x04},
+	{0x00, 0x0C},
+	{0x05, 0x08},
+	{0x06, 0x08},
+	{0x08, 0x08},
+	{0x09, 0x08},
+	{0x0A, 0xE6},
+	{0x0B, 0x8C},
+	{0x1A, 0x12},
+	{0x1E, 0xE0},
+	{0x29, 0x93},
+	{0x2A, 0x93},
+	{0x2F, 0x02},
+	{0x31, 0x02},
+	{0x33, 0x05},
+	{0x37, 0x2D},
+	{0x38, 0x2D},
+	{0x3A, 0x1E},
+	{0x3B, 0x1E},
+	{0x3D, 0x27},
+	{0x3F, 0x80},
+	{0x40, 0x40},
+	{0x41, 0xE0},
+	{0x4F, 0x2F},
+	{0x50, 0x1E},
+	{0xFE, 0x06},
+	{0x00, 0xCC},
+	{0x05, 0x05},
+	{0x07, 0xA2},
+	{0x08, 0xCC},
+	{0x0D, 0x03},
+	{0x0F, 0xA2},
+	{0x32, 0xCC},
+	{0x37, 0x05},
+	{0x39, 0x83},
+	{0x3A, 0xCC},
+	{0x41, 0x04},
+	{0x43, 0x83},
+	{0x44, 0xCC},
+	{0x49, 0x05},
+	{0x4B, 0xA2},
+	{0x4C, 0xCC},
+	{0x51, 0x03},
+	{0x53, 0xA2},
+	{0x75, 0xCC},
+	{0x7A, 0x03},
+	{0x7C, 0x83},
+	{0x7D, 0xCC},
+	{0x82, 0x02},
+	{0x84, 0x83},
+	{0x85, 0xEC},
+	{0x86, 0x0F},
+	{0x87, 0xFF},
+	{0x88, 0x00},
+	{0x8A, 0x02},
+	{0x8C, 0xA2},
+	{0x8D, 0xEA},
+	{0x8E, 0x01},
+	{0x8F, 0xE8},
+	{0xFE, 0x06},
+	{0x90, 0x0A},
+	{0x92, 0x06},
+	{0x93, 0xA0},
+	{0x94, 0xA8},
+	{0x95, 0xEC},
+	{0x96, 0x0F},
+	{0x97, 0xFF},
+	{0x98, 0x00},
+	{0x9A, 0x02},
+	{0x9C, 0xA2},
+	{0xAC, 0x04},
+	{0xFE, 0x06},
+	{0xB1, 0x12},
+	{0xB2, 0x17},
+	{0xB3, 0x17},
+	{0xB4, 0x17},
+	{0xB5, 0x17},
+	{0xB6, 0x11},
+	{0xB7, 0x08},
+	{0xB8, 0x09},
+	{0xB9, 0x06},
+	{0xBA, 0x07},
+	{0xBB, 0x17},
+	{0xBC, 0x17},
+	{0xBD, 0x17},
+	{0xBE, 0x17},
+	{0xBF, 0x17},
+	{0xC0, 0x17},
+	{0xC1, 0x17},
+	{0xC2, 0x17},
+	{0xC3, 0x17},
+	{0xC4, 0x0F},
+	{0xC5, 0x0E},
+	{0xC6, 0x00},
+	{0xC7, 0x01},
+	{0xC8, 0x10},
+	{0xFE, 0x06},
+	{0x95, 0xEC},
+	{0x8D, 0xEE},
+	{0x44, 0xEC},
+	{0x4C, 0xEC},
+	{0x32, 0xEC},
+	{0x3A, 0xEC},
+	{0x7D, 0xEC},
+	{0x75, 0xEC},
+	{0x00, 0xEC},
+	{0x08, 0xEC},
+	{0x85, 0xEC},
+	{0xA6, 0x21},
+	{0xA7, 0x05},
+	{0xA9, 0x06},
+	{0x82, 0x06},
+	{0x41, 0x06},
+	{0x7A, 0x07},
+	{0x37, 0x07},
+	{0x05, 0x06},
+	{0x49, 0x06},
+	{0x0D, 0x04},
+	{0x51, 0x04},
+};
+
+static const u32 rad_bus_formats[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_RGB666_1X18,
+	MEDIA_BUS_FMT_RGB565_1X16,
+};
+
+struct rad_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	struct gpio_desc *reset;
+	struct backlight_device *backlight;
+
+	bool prepared;
+	bool enabled;
+
+	struct videomode vm;
+	u32 width_mm;
+	u32 height_mm;
+};
+
+static inline struct rad_panel *to_rad_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rad_panel, base);
+}
+
+static int rad_panel_push_cmd_list(struct mipi_dsi_device *dsi)
+{
+	size_t i;
+	size_t count = ARRAY_SIZE(manufacturer_cmd_set);
+	int ret = 0;
+
+	for (i = 0; i < count; i++) {
+		const struct cmd_set_entry *entry = &manufacturer_cmd_set[i];
+		u8 buffer[2] = { entry->cmd, entry->param };
+
+		ret = mipi_dsi_generic_write(dsi, &buffer, sizeof(buffer));
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+};
+
+static int color_format_from_dsi_format(enum mipi_dsi_pixel_format format)
+{
+	switch (format) {
+	case MIPI_DSI_FMT_RGB565:
+		return 0x55;
+	case MIPI_DSI_FMT_RGB666:
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return 0x66;
+	case MIPI_DSI_FMT_RGB888:
+		return 0x77;
+	default:
+		return 0x77; /* for backward compatibility */
+	}
+};
+
+static int rad_panel_prepare(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+
+	if (rad->prepared)
+		return 0;
+
+	if (rad->reset) {
+		gpiod_set_value(rad->reset, 0);
+		usleep_range(5000, 10000);
+		gpiod_set_value(rad->reset, 1);
+		usleep_range(20000, 25000);
+	}
+
+	rad->prepared = true;
+
+	return 0;
+}
+
+static int rad_panel_unprepare(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+	struct device *dev = &rad->dsi->dev;
+
+	if (!rad->prepared)
+		return 0;
+
+	if (rad->enabled) {
+		DRM_DEV_ERROR(dev, "Panel still enabled!\n");
+		return -EPERM;
+	}
+
+	if (rad->reset) {
+		gpiod_set_value(rad->reset, 0);
+		usleep_range(15000, 17000);
+		gpiod_set_value(rad->reset, 1);
+	}
+
+	rad->prepared = false;
+
+	return 0;
+}
+
+static int rad_panel_enable(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+	struct mipi_dsi_device *dsi = rad->dsi;
+	struct device *dev = &dsi->dev;
+	int color_format = color_format_from_dsi_format(dsi->format);
+	u16 brightness;
+	int ret;
+
+	if (rad->enabled)
+		return 0;
+
+	if (!rad->prepared) {
+		DRM_DEV_ERROR(dev, "Panel not prepared!\n");
+		return -EPERM;
+	}
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	ret = rad_panel_push_cmd_list(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to send MCS (%d)\n", ret);
+		goto fail;
+	}
+
+	/* Select User Command Set table (CMD1) */
+	ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2);
+	if (ret < 0)
+		goto fail;
+
+	/* Software reset */
+	ret = mipi_dsi_dcs_soft_reset(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to do Software Reset (%d)\n", ret);
+		goto fail;
+	}
+
+	usleep_range(15000, 17000);
+
+	/* Set DSI mode */
+	ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set DSI mode (%d)\n", ret);
+		goto fail;
+	}
+	/* Set tear ON */
+	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set tear ON (%d)\n", ret);
+		goto fail;
+	}
+	/* Set tear scanline */
+	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set tear scanline (%d)\n", ret);
+		goto fail;
+	}
+	/* Set pixel format */
+	ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format);
+	DRM_DEV_DEBUG_DRIVER(dev, "Interface color format set to 0x%x\n",
+			     color_format);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set pixel format (%d)\n", ret);
+		goto fail;
+	}
+	/* Set display brightness */
+	brightness = rad->backlight->props.brightness;
+	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set display brightness (%d)\n",
+			      ret);
+		goto fail;
+	}
+	/* Exit sleep mode */
+	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to exit sleep mode (%d)\n", ret);
+		goto fail;
+	}
+
+	usleep_range(5000, 10000);
+
+	ret = mipi_dsi_dcs_set_display_on(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set display ON (%d)\n", ret);
+		goto fail;
+	}
+
+	backlight_enable(rad->backlight);
+
+	rad->enabled = true;
+
+	return 0;
+
+fail:
+	if (rad->reset)
+		gpiod_set_value(rad->reset, 0);
+
+	return ret;
+}
+
+static int rad_panel_disable(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+	struct mipi_dsi_device *dsi = rad->dsi;
+	struct device *dev = &dsi->dev;
+	int ret;
+
+	if (!rad->enabled)
+		return 0;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	backlight_disable(rad->backlight);
+
+	usleep_range(10000, 15000);
+
+	ret = mipi_dsi_dcs_set_display_off(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to set display OFF (%d)\n", ret);
+		return ret;
+	}
+
+	usleep_range(5000, 10000);
+
+	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "Failed to enter sleep mode (%d)\n", ret);
+		return ret;
+	}
+
+	rad->enabled = false;
+
+	return 0;
+}
+
+static int rad_panel_get_modes(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+	struct device *dev = &rad->dsi->dev;
+	struct drm_connector *connector = panel->connector;
+	struct drm_display_mode *mode;
+	u32 *bus_flags = &connector->display_info.bus_flags;
+	int ret;
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode) {
+		DRM_DEV_ERROR(dev, "Failed to create display mode!\n");
+		return 0;
+	}
+
+	drm_display_mode_from_videomode(&rad->vm, mode);
+	mode->width_mm = rad->width_mm;
+	mode->height_mm = rad->height_mm;
+	connector->display_info.width_mm = rad->width_mm;
+	connector->display_info.height_mm = rad->height_mm;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	if (rad->vm.flags & DISPLAY_FLAGS_DE_HIGH)
+		*bus_flags |= DRM_BUS_FLAG_DE_HIGH;
+	if (rad->vm.flags & DISPLAY_FLAGS_DE_LOW)
+		*bus_flags |= DRM_BUS_FLAG_DE_LOW;
+	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+		*bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+		*bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
+
+	ret = drm_display_info_set_bus_formats(&connector->display_info,
+					       rad_bus_formats,
+					       ARRAY_SIZE(rad_bus_formats));
+	if (ret)
+		return ret;
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	return 1;
+}
+
+static int rad_bl_get_brightness(struct backlight_device *bl)
+{
+	struct mipi_dsi_device *dsi = bl_get_data(bl);
+	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+	struct device *dev = &dsi->dev;
+	u16 brightness;
+	int ret;
+
+	if (!rad->prepared)
+		return 0;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "\n");
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+	if (ret < 0)
+		return ret;
+
+	bl->props.brightness = brightness;
+
+	return brightness & 0xff;
+}
+
+static int rad_bl_update_status(struct backlight_device *bl)
+{
+	struct mipi_dsi_device *dsi = bl_get_data(bl);
+	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+	struct device *dev = &dsi->dev;
+	int ret = 0;
+
+	if (!rad->prepared)
+		return 0;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "New brightness: %d\n", bl->props.brightness);
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct backlight_ops rad_bl_ops = {
+	.update_status = rad_bl_update_status,
+	.get_brightness = rad_bl_get_brightness,
+};
+
+static const struct drm_panel_funcs rad_panel_funcs = {
+	.prepare = rad_panel_prepare,
+	.unprepare = rad_panel_unprepare,
+	.enable = rad_panel_enable,
+	.disable = rad_panel_disable,
+	.get_modes = rad_panel_get_modes,
+};
+
+/*
+ * The clock might range from 66MHz (30Hz refresh rate)
+ * to 132MHz (60Hz refresh rate)
+ */
+static const struct display_timing rad_default_timing = {
+	.pixelclock = { 66000000, 132000000, 132000000 },
+	.hactive = { 1080, 1080, 1080 },
+	.hfront_porch = { 20, 20, 20 },
+	.hsync_len = { 2, 2, 2 },
+	.hback_porch = { 34, 34, 34 },
+	.vactive = { 1920, 1920, 1920 },
+	.vfront_porch = { 10, 10, 10 },
+	.vsync_len = { 2, 2, 2 },
+	.vback_porch = { 4, 4, 4 },
+	.flags = DISPLAY_FLAGS_HSYNC_LOW |
+		 DISPLAY_FLAGS_VSYNC_LOW |
+		 DISPLAY_FLAGS_DE_LOW |
+		 DISPLAY_FLAGS_PIXDATA_NEGEDGE,
+};
+
+static int rad_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *timings;
+	struct rad_panel *panel;
+	struct backlight_properties bl_props;
+	int ret;
+	u32 video_mode;
+
+	panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
+	if (!panel)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, panel);
+
+	panel->dsi = dsi;
+
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+			   MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ret = of_property_read_u32(np, "video-mode", &video_mode);
+	if (!ret) {
+		switch (video_mode) {
+		case 0:
+			/* burst mode */
+			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
+			break;
+		case 1:
+			/* non-burst mode with sync event */
+			break;
+		case 2:
+			/* non-burst mode with sync pulse */
+			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+			break;
+		default:
+			dev_warn(dev, "invalid video mode %d\n", video_mode);
+			break;
+		}
+	}
+
+	ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes);
+	if (ret < 0) {
+		dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret);
+		return ret;
+	}
+
+	/*
+	 * 'display-timings' is optional, so verify if the node is present
+	 * before calling of_get_videomode so we won't get console error
+	 * messages
+	 */
+	timings = of_get_child_by_name(np, "display-timings");
+	if (timings) {
+		of_node_put(timings);
+		ret = of_get_videomode(np, &panel->vm, 0);
+	} else {
+		videomode_from_timing(&rad_default_timing, &panel->vm);
+	}
+	if (ret < 0)
+		return ret;
+
+	of_property_read_u32(np, "width-mm", &panel->width_mm);
+	of_property_read_u32(np, "height-mm", &panel->height_mm);
+
+	panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+
+	if (IS_ERR(panel->reset))
+		panel->reset = NULL;
+	else
+		gpiod_set_value(panel->reset, 0);
+
+	memset(&bl_props, 0, sizeof(bl_props));
+	bl_props.type = BACKLIGHT_RAW;
+	bl_props.brightness = 255;
+	bl_props.max_brightness = 255;
+
+	panel->backlight = devm_backlight_device_register(dev, dev_name(dev),
+							  dev, dsi,
+							  &rad_bl_ops,
+							  &bl_props);
+	if (IS_ERR(panel->backlight)) {
+		ret = PTR_ERR(panel->backlight);
+		dev_err(dev, "Failed to register backlight (%d)\n", ret);
+		return ret;
+	}
+
+	drm_panel_init(&panel->base);
+	panel->base.funcs = &rad_panel_funcs;
+	panel->base.dev = dev;
+	dev_set_drvdata(dev, panel);
+
+	ret = drm_panel_add(&panel->base);
+
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0)
+		drm_panel_remove(&panel->base);
+
+	return ret;
+}
+
+static int rad_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+	struct device *dev = &dsi->dev;
+	int ret;
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		DRM_DEV_ERROR(dev, "Failed to detach from host (%d)\n",
+			      ret);
+
+	drm_panel_remove(&rad->base);
+
+	return 0;
+}
+
+static void rad_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+
+	rad_panel_disable(&rad->base);
+	rad_panel_unprepare(&rad->base);
+}
+
+#ifdef CONFIG_PM
+static int rad_panel_suspend(struct device *dev)
+{
+	struct rad_panel *rad = dev_get_drvdata(dev);
+
+	if (!rad->reset)
+		return 0;
+
+	devm_gpiod_put(dev, rad->reset);
+	rad->reset = NULL;
+
+	return 0;
+}
+
+static int rad_panel_resume(struct device *dev)
+{
+	struct rad_panel *rad = dev_get_drvdata(dev);
+
+	if (rad->reset)
+		return 0;
+
+	rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(rad->reset))
+		rad->reset = NULL;
+
+	return PTR_ERR_OR_ZERO(rad->reset);
+}
+
+#endif
+
+static const struct dev_pm_ops rad_pm_ops = {
+	SET_RUNTIME_PM_OPS(rad_panel_suspend, rad_panel_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(rad_panel_suspend, rad_panel_resume)
+};
+
+static const struct of_device_id rad_of_match[] = {
+	{ .compatible = "raydium,rm67191", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rad_of_match);
+
+static struct mipi_dsi_driver rad_panel_driver = {
+	.driver = {
+		.name = "panel-raydium-rm67191",
+		.of_match_table = rad_of_match,
+		.pm	= &rad_pm_ops,
+	},
+	.probe = rad_panel_probe,
+	.remove = rad_panel_remove,
+	.shutdown = rad_panel_shutdown,
+};
+module_mipi_dsi_driver(rad_panel_driver);
+
+MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");
+MODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
@ 2019-06-14 12:03   ` Daniel Baluta
  2019-06-14 12:27     ` Fabio Estevam
  2019-06-14 15:10     ` Sam Ravnborg
  2 siblings, 0 replies; 15+ messages in thread
From: Daniel Baluta @ 2019-06-14 12:03 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland, dri-devel, Devicetree List,
	Linux Kernel Mailing List, dl-linux-imx

Hi Robert,

Minor comment. See inline:

On Fri, Jun 14, 2019 at 2:52 PM Robert Chiras <robert.chiras@nxp.com> wrote:
>
> This patch adds Raydium RM67191 TFT LCD panel driver (MIPI-DSI
> protocol).
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
>  drivers/gpu/drm/panel/Kconfig                 |   9 +
>  drivers/gpu/drm/panel/Makefile                |   1 +
>  drivers/gpu/drm/panel/panel-raydium-rm67191.c | 730 ++++++++++++++++++++++++++
>  3 files changed, 740 insertions(+)
>  create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index d9d931a..8be1ac1 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -159,6 +159,15 @@ config DRM_PANEL_RASPBERRYPI_TOUCHSCREEN
>           Pi 7" Touchscreen.  To compile this driver as a module,
>           choose M here.
>
> +config DRM_PANEL_RAYDIUM_RM67191
> +       tristate "Raydium RM67191 FHD 1080x1920 DSI video mode 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 Raydium RM67191 FHD
> +         (1080x1920) DSI panel.
> +
>  config DRM_PANEL_RAYDIUM_RM68200
>         tristate "Raydium RM68200 720x1280 DSI video mode panel"
>         depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index fb0cb3a..1fc0f68 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
>  obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o
>  obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
>  obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
> +obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o
>  obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
>  obj-$(CONFIG_DRM_PANEL_ROCKTECH_JH057N00900) += panel-rocktech-jh057n00900.o
>  obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
> diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> new file mode 100644
> index 0000000..75bfb03
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> @@ -0,0 +1,730 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * i.MX drm driver - Raydium MIPI-DSI panel driver
> + *
> + * Copyright (C) 2017 NXP
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */

Please remove the license text once you already added the SPDX identifier.

Also preferred copyright for NXP is:

Copyright 2019 NXP

So, the file should look like this:

// SPDX-License-Identifier: GPL-2.0
/*
 * i.MX drm driver - Raydium MIPI-DSI panel driver
 *
 * Copyright 2019 NXP
 */

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
@ 2019-06-14 12:27     ` Fabio Estevam
  2019-06-14 12:27     ` Fabio Estevam
  2019-06-14 15:10     ` Sam Ravnborg
  2 siblings, 0 replies; 15+ messages in thread
From: Fabio Estevam @ 2019-06-14 12:27 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland, DRI mailing list,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel, NXP Linux Team

Hi Robert,

On Fri, Jun 14, 2019 at 8:52 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> @@ -0,0 +1,730 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * i.MX drm driver - Raydium MIPI-DSI panel driver
> + *
> + * Copyright (C) 2017 NXP
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

No need for this text as you are using SPDX tag.

> +static int color_format_from_dsi_format(enum mipi_dsi_pixel_format format)
> +{
> +       switch (format) {
> +       case MIPI_DSI_FMT_RGB565:
> +               return 0x55;
> +       case MIPI_DSI_FMT_RGB666:
> +       case MIPI_DSI_FMT_RGB666_PACKED:
> +               return 0x66;
> +       case MIPI_DSI_FMT_RGB888:
> +               return 0x77;

Could you use defines for these magic 0x55, 0x66 and 0x77 numbers?

> +static int rad_panel_prepare(struct drm_panel *panel)
> +{
> +       struct rad_panel *rad = to_rad_panel(panel);
> +
> +       if (rad->prepared)
> +               return 0;
> +
> +       if (rad->reset) {
> +               gpiod_set_value(rad->reset, 0);
> +               usleep_range(5000, 10000);
> +               gpiod_set_value(rad->reset, 1);
> +               usleep_range(20000, 25000);

This does not look correct.

The correct way to do a reset with gpiod API is:

 gpiod_set_value(rad->reset, 1);

delay

gpiod_set_value(rad->reset, 0);

I don't have the datasheet for the RM67191 panel, but I assume the
reset GPIO is active low.

Since you inverted the polarity in the dts and inside the driver, you
got it right by accident.

You could also consider using gpiod_set_value_cansleep() variant
instead because the GPIO reset could be provided by an I2C GPIO
expander, for example.

Also, when sleeping for more than 10ms, msleep is a better fit as per
Documentation/timers/timers-howto.txt.

> +       if (rad->reset) {
> +               gpiod_set_value(rad->reset, 0);
> +               usleep_range(15000, 17000);
> +               gpiod_set_value(rad->reset, 1);
> +       }

Another reset?

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
@ 2019-06-14 12:27     ` Fabio Estevam
  0 siblings, 0 replies; 15+ messages in thread
From: Fabio Estevam @ 2019-06-14 12:27 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	David Airlie, linux-kernel, DRI mailing list, Rob Herring,
	Thierry Reding, NXP Linux Team, Sam Ravnborg

Hi Robert,

On Fri, Jun 14, 2019 at 8:52 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> @@ -0,0 +1,730 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * i.MX drm driver - Raydium MIPI-DSI panel driver
> + *
> + * Copyright (C) 2017 NXP
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

No need for this text as you are using SPDX tag.

> +static int color_format_from_dsi_format(enum mipi_dsi_pixel_format format)
> +{
> +       switch (format) {
> +       case MIPI_DSI_FMT_RGB565:
> +               return 0x55;
> +       case MIPI_DSI_FMT_RGB666:
> +       case MIPI_DSI_FMT_RGB666_PACKED:
> +               return 0x66;
> +       case MIPI_DSI_FMT_RGB888:
> +               return 0x77;

Could you use defines for these magic 0x55, 0x66 and 0x77 numbers?

> +static int rad_panel_prepare(struct drm_panel *panel)
> +{
> +       struct rad_panel *rad = to_rad_panel(panel);
> +
> +       if (rad->prepared)
> +               return 0;
> +
> +       if (rad->reset) {
> +               gpiod_set_value(rad->reset, 0);
> +               usleep_range(5000, 10000);
> +               gpiod_set_value(rad->reset, 1);
> +               usleep_range(20000, 25000);

This does not look correct.

The correct way to do a reset with gpiod API is:

 gpiod_set_value(rad->reset, 1);

delay

gpiod_set_value(rad->reset, 0);

I don't have the datasheet for the RM67191 panel, but I assume the
reset GPIO is active low.

Since you inverted the polarity in the dts and inside the driver, you
got it right by accident.

You could also consider using gpiod_set_value_cansleep() variant
instead because the GPIO reset could be provided by an I2C GPIO
expander, for example.

Also, when sleeping for more than 10ms, msleep is a better fit as per
Documentation/timers/timers-howto.txt.

> +       if (rad->reset) {
> +               gpiod_set_value(rad->reset, 0);
> +               usleep_range(15000, 17000);
> +               gpiod_set_value(rad->reset, 1);
> +       }

Another reset?
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-14 11:51 ` [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
@ 2019-06-14 12:59     ` Fabio Estevam
  0 siblings, 0 replies; 15+ messages in thread
From: Fabio Estevam @ 2019-06-14 12:59 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland, DRI mailing list,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel, NXP Linux Team

On Fri, Jun 14, 2019 at 8:53 AM Robert Chiras <robert.chiras@nxp.com> wrote:
>
> Add dt-bindings documentation for Raydium RM67191 DSI panel.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
>  .../bindings/display/panel/raydium,rm67191.txt     | 42 ++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
>
> diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> new file mode 100644
> index 0000000..5a6268d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> @@ -0,0 +1,42 @@
> +Raydium RM67171 OLED LCD panel with MIPI-DSI protocol
> +
> +Required properties:
> +- compatible:          "raydium,rm67191"
> +- reg:                 virtual channel for MIPI-DSI protocol
> +                       must be <0>
> +- dsi-lanes:           number of DSI lanes to be used
> +                       must be <3> or <4>
> +- port:                input port node with endpoint definition as
> +                       defined in Documentation/devicetree/bindings/graph.txt;
> +                       the input port should be connected to a MIPI-DSI device
> +                       driver
> +
> +Optional properties:
> +- reset-gpio:          a GPIO spec for the RST_B GPIO pin

reset-gpios (with the s in the end) is the recommendation.

> +- display-timings:     timings for the connected panel according to [1]

This is not needed.

> +- video-mode:          0 - burst-mode
> +                       1 - non-burst with sync event
> +                       2 - non-burst with sync pulse
> +
> +[1]: Documentation/devicetree/bindings/display/display-timing.txt

This path does not exist.

Also, could you try to align these bindings with the one from raydium,rm68200?

There are power-supply and backlight optional properties there, which
seem useful.

> +
> +Example:
> +
> +       panel@0 {
> +               compatible = "raydium,rm67191";
> +               reg = <0>;
> +               pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;

You should also pass pinctrl-names = "default"; if you use pinctrl-0.

> +               reset-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;

Should be active low.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
@ 2019-06-14 12:59     ` Fabio Estevam
  0 siblings, 0 replies; 15+ messages in thread
From: Fabio Estevam @ 2019-06-14 12:59 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Thierry Reding, Sam Ravnborg, David Airlie, Daniel Vetter,
	Rob Herring, Mark Rutland, DRI mailing list,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel, NXP Linux Team

On Fri, Jun 14, 2019 at 8:53 AM Robert Chiras <robert.chiras@nxp.com> wrote:
>
> Add dt-bindings documentation for Raydium RM67191 DSI panel.
>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
>  .../bindings/display/panel/raydium,rm67191.txt     | 42 ++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
>
> diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> new file mode 100644
> index 0000000..5a6268d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> @@ -0,0 +1,42 @@
> +Raydium RM67171 OLED LCD panel with MIPI-DSI protocol
> +
> +Required properties:
> +- compatible:          "raydium,rm67191"
> +- reg:                 virtual channel for MIPI-DSI protocol
> +                       must be <0>
> +- dsi-lanes:           number of DSI lanes to be used
> +                       must be <3> or <4>
> +- port:                input port node with endpoint definition as
> +                       defined in Documentation/devicetree/bindings/graph.txt;
> +                       the input port should be connected to a MIPI-DSI device
> +                       driver
> +
> +Optional properties:
> +- reset-gpio:          a GPIO spec for the RST_B GPIO pin

reset-gpios (with the s in the end) is the recommendation.

> +- display-timings:     timings for the connected panel according to [1]

This is not needed.

> +- video-mode:          0 - burst-mode
> +                       1 - non-burst with sync event
> +                       2 - non-burst with sync pulse
> +
> +[1]: Documentation/devicetree/bindings/display/display-timing.txt

This path does not exist.

Also, could you try to align these bindings with the one from raydium,rm68200?

There are power-supply and backlight optional properties there, which
seem useful.

> +
> +Example:
> +
> +       panel@0 {
> +               compatible = "raydium,rm67191";
> +               reg = <0>;
> +               pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;

You should also pass pinctrl-names = "default"; if you use pinctrl-0.

> +               reset-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;

Should be active low.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [EXT] Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 12:27     ` Fabio Estevam
  (?)
@ 2019-06-14 13:29     ` Robert Chiras
  2019-06-14 13:39       ` Fabio Estevam
  -1 siblings, 1 reply; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 13:29 UTC (permalink / raw)
  To: festevam
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, sam, daniel,
	mark.rutland, thierry.reding, dri-devel, airlied

Hi Fabio,

On Vi, 2019-06-14 at 09:27 -0300, Fabio Estevam wrote:
> Hi Robert,
> 
> On Fri, Jun 14, 2019 at 8:52 AM Robert Chiras <robert.chiras@nxp.com>
> wrote:
> 
> > 
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> > @@ -0,0 +1,730 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * i.MX drm driver - Raydium MIPI-DSI panel driver
> > + *
> > + * Copyright (C) 2017 NXP
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> No need for this text as you are using SPDX tag.
> 
> > 
> > +static int color_format_from_dsi_format(enum mipi_dsi_pixel_format
> > format)
> > +{
> > +       switch (format) {
> > +       case MIPI_DSI_FMT_RGB565:
> > +               return 0x55;
> > +       case MIPI_DSI_FMT_RGB666:
> > +       case MIPI_DSI_FMT_RGB666_PACKED:
> > +               return 0x66;
> > +       case MIPI_DSI_FMT_RGB888:
> > +               return 0x77;
> Could you use defines for these magic 0x55, 0x66 and 0x77 numbers?
Those magic numbers mean exactly what their case statements are. They
come from the panel documentation. I thought that the already existing
defines (MIPI_DSI_FMT_) are self explanatory here, so using defines
seemed redundant for me. But, if you think that using defines for them
is better, I can do that.
> 
> > 
> > +static int rad_panel_prepare(struct drm_panel *panel)
> > +{
> > +       struct rad_panel *rad = to_rad_panel(panel);
> > +
> > +       if (rad->prepared)
> > +               return 0;
> > +
> > +       if (rad->reset) {
> > +               gpiod_set_value(rad->reset, 0);
> > +               usleep_range(5000, 10000);
> > +               gpiod_set_value(rad->reset, 1);
> > +               usleep_range(20000, 25000);
> This does not look correct.
> 
> The correct way to do a reset with gpiod API is:
> 
>  gpiod_set_value(rad->reset, 1);
> 
> delay
> 
> gpiod_set_value(rad->reset, 0);
> 
> I don't have the datasheet for the RM67191 panel, but I assume the
> reset GPIO is active low.
> 
> Since you inverted the polarity in the dts and inside the driver, you
> got it right by accident.
The GPIO is active high, and the above sequence was received from the
panel vendor in the following form:
	SET_RESET_PIN(1);
	MDELAY(10);
	SET_RESET_PIN(0);
	MDELAY(5);
	SET_RESET_PIN(1);
	MDELAY(20);
I got rid of the first transition to high since seemed redundant.
Also, according to the manual reference, the RSTB pin needs to be
active high while operating the display.
> 
> You could also consider using gpiod_set_value_cansleep() variant
> instead because the GPIO reset could be provided by an I2C GPIO
> expander, for example.
Thanks, will use this in the next revision.
> 
> Also, when sleeping for more than 10ms, msleep is a better fit as per
> Documentation/timers/timers-howto.txt.
OK, I will use msleep. That documentation recommends using usleep_range
for sleeps <20m, but also using msleep for >10ms (so I followed the
recomendations from usleep_range)
> 
> > 
> > +       if (rad->reset) {
> > +               gpiod_set_value(rad->reset, 0);
> > +               usleep_range(15000, 17000);
> > +               gpiod_set_value(rad->reset, 1);
> > +       }
> Another reset?
Yes. This is tricky, since this GPIO is shared between the DSI
controller and touch controller. The Android guys needs the touch
controller to be active, while the display can be turned off. So this
is why, after the display is turned off, the reset pin is put back to
HIGH in order to keep the touch working.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [EXT] Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 13:29     ` [EXT] " Robert Chiras
@ 2019-06-14 13:39       ` Fabio Estevam
  2019-06-14 13:50         ` Robert Chiras
  0 siblings, 1 reply; 15+ messages in thread
From: Fabio Estevam @ 2019-06-14 13:39 UTC (permalink / raw)
  To: Robert Chiras
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, sam, daniel,
	mark.rutland, thierry.reding, dri-devel, airlied

On Fri, Jun 14, 2019 at 10:29 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> The GPIO is active high, and the above sequence was received from the
> panel vendor in the following form:
>         SET_RESET_PIN(1);
>         MDELAY(10);
>         SET_RESET_PIN(0);
>         MDELAY(5);
>         SET_RESET_PIN(1);
>         MDELAY(20);
> I got rid of the first transition to high since seemed redundant.
> Also, according to the manual reference, the RSTB pin needs to be
> active high while operating the display.

That's exactly my point :-)

In normal operation the GPIO reset needs to be high.

During reset the GPIO reset needs to be low., which means that the
GPIO reset is "active low".

So you should invert both the dts and the driver to behave correctly.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [EXT] Re: [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-14 12:59     ` Fabio Estevam
  (?)
@ 2019-06-14 13:40     ` Robert Chiras
  -1 siblings, 0 replies; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 13:40 UTC (permalink / raw)
  To: festevam
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, sam, daniel,
	mark.rutland, thierry.reding, dri-devel, airlied

Hi Fabio,

On Vi, 2019-06-14 at 09:59 -0300, Fabio Estevam wrote:
> On Fri, Jun 14, 2019 at 8:53 AM Robert Chiras <robert.chiras@nxp.com>
> wrote:
> > 
> > 
> > Add dt-bindings documentation for Raydium RM67191 DSI panel.
> > 
> > Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> > ---
> >  .../bindings/display/panel/raydium,rm67191.txt     | 42
> > ++++++++++++++++++++++
> >  1 file changed, 42 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> > 
> > diff --git
> > a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.t
> > xt
> > b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.t
> > xt
> > new file mode 100644
> > index 0000000..5a6268d
> > --- /dev/null
> > +++
> > b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.t
> > xt
> > @@ -0,0 +1,42 @@
> > +Raydium RM67171 OLED LCD panel with MIPI-DSI protocol
> > +
> > +Required properties:
> > +- compatible:          "raydium,rm67191"
> > +- reg:                 virtual channel for MIPI-DSI protocol
> > +                       must be <0>
> > +- dsi-lanes:           number of DSI lanes to be used
> > +                       must be <3> or <4>
> > +- port:                input port node with endpoint definition as
> > +                       defined in
> > Documentation/devicetree/bindings/graph.txt;
> > +                       the input port should be connected to a
> > MIPI-DSI device
> > +                       driver
> > +
> > +Optional properties:
> > +- reset-gpio:          a GPIO spec for the RST_B GPIO pin
> reset-gpios (with the s in the end) is the recommendation.
> 
> > 
> > +- display-timings:     timings for the connected panel according
> > to [1]
> This is not needed.
Well, I know that the panel timings are already hard-coded into the
driver, but on 850D, we have two display controllers: eLCDDIF and DCSS.
While eLCDIF works just fine with the display-timings received (and
undocumented) from panel vendor, with DCSS we had some issues and we
had to tweak the display-timings. This is why I added this property,
for a special case where we have to use different timings without
changing the driver (just a different dtb file). Do you think this is a
bad practice? If yes, then what mechanism of doing that do you
recommend?
> 
> > 
> > +- video-mode:          0 - burst-mode
> > +                       1 - non-burst with sync event
> > +                       2 - non-burst with sync pulse
> > +
> > +[1]: Documentation/devicetree/bindings/display/display-timing.txt
> This path does not exist.
Right. Will update the path.
> 
> Also, could you try to align these bindings with the one from
> raydium,rm68200?
> 
> There are power-supply and backlight optional properties there, which
> seem useful.
This panel is OLED, while the rm68200 is LCD (from what I've noticed).
Meaning this panel backligth is also controlled by the DSI controller,
not by a separate backlight LED driver.
I will consider, instead, adding support for a power-supply (if
possible).
> 
> > 
> > +
> > +Example:
> > +
> > +       panel@0 {
> > +               compatible = "raydium,rm67191";
> > +               reg = <0>;
> > +               pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;
> You should also pass pinctrl-names = "default"; if you use pinctrl-0.
Thanks. Will do that
> 
> > 
> > +               reset-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
> Should be active low.
But, the GPIO is active high.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [EXT] Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 13:39       ` Fabio Estevam
@ 2019-06-14 13:50         ` Robert Chiras
  0 siblings, 0 replies; 15+ messages in thread
From: Robert Chiras @ 2019-06-14 13:50 UTC (permalink / raw)
  To: festevam
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, sam, daniel,
	mark.rutland, thierry.reding, dri-devel, airlied

On Vi, 2019-06-14 at 10:39 -0300, Fabio Estevam wrote:
> Caution: EXT Email
> 
> On Fri, Jun 14, 2019 at 10:29 AM Robert Chiras <robert.chiras@nxp.com
> > wrote:
> 
> > 
> > The GPIO is active high, and the above sequence was received from
> > the
> > panel vendor in the following form:
> >         SET_RESET_PIN(1);
> >         MDELAY(10);
> >         SET_RESET_PIN(0);
> >         MDELAY(5);
> >         SET_RESET_PIN(1);
> >         MDELAY(20);
> > I got rid of the first transition to high since seemed redundant.
> > Also, according to the manual reference, the RSTB pin needs to be
> > active high while operating the display.
> That's exactly my point :-)
> 
> In normal operation the GPIO reset needs to be high.
> 
> During reset the GPIO reset needs to be low., which means that the
> GPIO reset is "active low".
> 
> So you should invert both the dts and the driver to behave correctly.
Now I get it. Thanks! I will update the dts and driver for the gpio.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
@ 2019-06-14 15:10     ` Sam Ravnborg
  2019-06-14 12:27     ` Fabio Estevam
  2019-06-14 15:10     ` Sam Ravnborg
  2 siblings, 0 replies; 15+ messages in thread
From: Sam Ravnborg @ 2019-06-14 15:10 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Thierry Reding, David Airlie, Daniel Vetter, Rob Herring,
	Mark Rutland, dri-devel, devicetree, linux-kernel, linux-imx

Hi Robert.

On top of the feedback from Fabio here is a bit more.

> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.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 <video/of_videomode.h>
> +#include <video/videomode.h>

Divide include up in block in following order:

#include <linux/*>

#include <video/*>

#incude <drm/*>

Within each block sort alphabetically.
Do not use the deprecated drmP.h - replace it with the necessary
includes.
Use an empty line between each include block.

> +
> +/* Write Manufacture Command Set Control */
> +#define WRMAUCCTR 0xFE
> +
> +/* Manufacturer Command Set pages (CMD2) */
> +struct cmd_set_entry {
> +	u8 cmd;
> +	u8 param;
> +};
> +
> +/*
> + * There is no description in the Reference Manual about these commands.
> + * We received them from vendor, so just use them as is.
> + */
> +static const struct cmd_set_entry manufacturer_cmd_set[] = {
> +	{0xFE, 0x0B},
> +	{0x28, 0x40},
> +	{0x29, 0x4F},
...
> +	{0x51, 0x04},
> +};
> +
> +static const u32 rad_bus_formats[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_RGB666_1X18,
> +	MEDIA_BUS_FMT_RGB565_1X16,
> +};
> +
> +struct rad_panel {
> +	struct drm_panel base;
In the other raydium driver we name this "panel", which is a more
descriptive name.

> +	struct mipi_dsi_device *dsi;
> +
> +	struct gpio_desc *reset;
> +	struct backlight_device *backlight;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	struct videomode vm;
> +	u32 width_mm;
> +	u32 height_mm;
> +};
> +
> +static int rad_panel_prepare(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +
> +	if (rad->prepared)
> +		return 0;
> +
> +	if (rad->reset) {
> +		gpiod_set_value(rad->reset, 0);
> +		usleep_range(5000, 10000);
> +		gpiod_set_value(rad->reset, 1);
> +		usleep_range(20000, 25000);
> +	}
> +
> +	rad->prepared = true;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct device *dev = &rad->dsi->dev;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	if (rad->enabled) {
> +		DRM_DEV_ERROR(dev, "Panel still enabled!\n");
> +		return -EPERM;
> +	}
This seems like overkill, what should trigger this?

> +
> +	if (rad->reset) {
> +		gpiod_set_value(rad->reset, 0);
> +		usleep_range(15000, 17000);
> +		gpiod_set_value(rad->reset, 1);
> +	}
> +
> +	rad->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_enable(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct mipi_dsi_device *dsi = rad->dsi;
> +	struct device *dev = &dsi->dev;
> +	int color_format = color_format_from_dsi_format(dsi->format);
> +	u16 brightness;
> +	int ret;
> +
> +	if (rad->enabled)
> +		return 0;
> +
> +	if (!rad->prepared) {
> +		DRM_DEV_ERROR(dev, "Panel not prepared!\n");
> +		return -EPERM;
> +	}
Seems like overkill.

> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = rad_panel_push_cmd_list(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to send MCS (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	/* Select User Command Set table (CMD1) */
> +	ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2);
> +	if (ret < 0)
> +		goto fail;
> +
> +	/* Software reset */
> +	ret = mipi_dsi_dcs_soft_reset(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to do Software Reset (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	usleep_range(15000, 17000);
> +
> +	/* Set DSI mode */
> +	ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set DSI mode (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set tear ON */
> +	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set tear ON (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set tear scanline */
> +	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set tear scanline (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set pixel format */
> +	ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format);
> +	DRM_DEV_DEBUG_DRIVER(dev, "Interface color format set to 0x%x\n",
> +			     color_format);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set pixel format (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set display brightness */
> +	brightness = rad->backlight->props.brightness;
> +	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display brightness (%d)\n",
> +			      ret);
> +		goto fail;
> +	}
brightness is written to again when you enable backlight below.
Seems redundant to do so twice.


> +	/* Exit sleep mode */
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to exit sleep mode (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	usleep_range(5000, 10000);
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display ON (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	backlight_enable(rad->backlight);
> +
> +	rad->enabled = true;
> +
> +	return 0;
> +
> +fail:
> +	if (rad->reset)
> +		gpiod_set_value(rad->reset, 0);
> +
> +	return ret;
> +}
> +
> +static int rad_panel_disable(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct mipi_dsi_device *dsi = rad->dsi;
> +	struct device *dev = &dsi->dev;
> +	int ret;
> +
> +	if (!rad->enabled)
> +		return 0;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	backlight_disable(rad->backlight);
> +
> +	usleep_range(10000, 15000);
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display OFF (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	usleep_range(5000, 10000);
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to enter sleep mode (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	rad->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct device *dev = &rad->dsi->dev;
> +	struct drm_connector *connector = panel->connector;
> +	struct drm_display_mode *mode;
> +	u32 *bus_flags = &connector->display_info.bus_flags;
> +	int ret;
> +
> +	mode = drm_mode_create(connector->dev);
> +	if (!mode) {
> +		DRM_DEV_ERROR(dev, "Failed to create display mode!\n");
> +		return 0;
> +	}
> +
> +	drm_display_mode_from_videomode(&rad->vm, mode);
> +	mode->width_mm = rad->width_mm;
> +	mode->height_mm = rad->height_mm;
> +	connector->display_info.width_mm = rad->width_mm;
> +	connector->display_info.height_mm = rad->height_mm;
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +
> +	if (rad->vm.flags & DISPLAY_FLAGS_DE_HIGH)
> +		*bus_flags |= DRM_BUS_FLAG_DE_HIGH;
> +	if (rad->vm.flags & DISPLAY_FLAGS_DE_LOW)
> +		*bus_flags |= DRM_BUS_FLAG_DE_LOW;
> +	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
> +		*bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
> +	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
> +		*bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
> +
> +	ret = drm_display_info_set_bus_formats(&connector->display_info,
> +					       rad_bus_formats,
> +					       ARRAY_SIZE(rad_bus_formats));
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static int rad_bl_get_brightness(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	u16 brightness;
> +	int ret;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	DRM_DEV_DEBUG_DRIVER(dev, "\n");
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	bl->props.brightness = brightness;
> +
> +	return brightness & 0xff;
> +}
> +
> +static int rad_bl_update_status(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	int ret = 0;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	DRM_DEV_DEBUG_DRIVER(dev, "New brightness: %d\n", bl->props.brightness);
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops rad_bl_ops = {
> +	.update_status = rad_bl_update_status,
> +	.get_brightness = rad_bl_get_brightness,
> +};
> +
> +static const struct drm_panel_funcs rad_panel_funcs = {
> +	.prepare = rad_panel_prepare,
> +	.unprepare = rad_panel_unprepare,
> +	.enable = rad_panel_enable,
> +	.disable = rad_panel_disable,
> +	.get_modes = rad_panel_get_modes,
> +};
> +
> +/*
> + * The clock might range from 66MHz (30Hz refresh rate)
> + * to 132MHz (60Hz refresh rate)
> + */
> +static const struct display_timing rad_default_timing = {
> +	.pixelclock = { 66000000, 132000000, 132000000 },
> +	.hactive = { 1080, 1080, 1080 },
> +	.hfront_porch = { 20, 20, 20 },
> +	.hsync_len = { 2, 2, 2 },
> +	.hback_porch = { 34, 34, 34 },
> +	.vactive = { 1920, 1920, 1920 },
> +	.vfront_porch = { 10, 10, 10 },
> +	.vsync_len = { 2, 2, 2 },
> +	.vback_porch = { 4, 4, 4 },
> +	.flags = DISPLAY_FLAGS_HSYNC_LOW |
> +		 DISPLAY_FLAGS_VSYNC_LOW |
> +		 DISPLAY_FLAGS_DE_LOW |
> +		 DISPLAY_FLAGS_PIXDATA_NEGEDGE,
> +};
> +
> +static int rad_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *timings;
> +	struct rad_panel *panel;
> +	struct backlight_properties bl_props;
> +	int ret;
> +	u32 video_mode;
> +
> +	panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
> +	if (!panel)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, panel);
> +
> +	panel->dsi = dsi;
> +
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			   MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	ret = of_property_read_u32(np, "video-mode", &video_mode);
> +	if (!ret) {
> +		switch (video_mode) {
> +		case 0:
> +			/* burst mode */
> +			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
> +			break;
> +		case 1:
> +			/* non-burst mode with sync event */
> +			break;
> +		case 2:
> +			/* non-burst mode with sync pulse */
> +			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> +			break;
> +		default:
> +			dev_warn(dev, "invalid video mode %d\n", video_mode);
> +			break;
> +		}
> +	}
> +
> +	ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * 'display-timings' is optional, so verify if the node is present
> +	 * before calling of_get_videomode so we won't get console error
> +	 * messages
> +	 */
> +	timings = of_get_child_by_name(np, "display-timings");
> +	if (timings) {
> +		of_node_put(timings);
> +		ret = of_get_videomode(np, &panel->vm, 0);
> +	} else {
> +		videomode_from_timing(&rad_default_timing, &panel->vm);
> +	}
> +	if (ret < 0)
> +		return ret;
> +
> +	of_property_read_u32(np, "width-mm", &panel->width_mm);
> +	of_property_read_u32(np, "height-mm", &panel->height_mm);
> +
> +	panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> +
> +	if (IS_ERR(panel->reset))
> +		panel->reset = NULL;
> +	else
> +		gpiod_set_value(panel->reset, 0);
> +
> +	memset(&bl_props, 0, sizeof(bl_props));
> +	bl_props.type = BACKLIGHT_RAW;
> +	bl_props.brightness = 255;
> +	bl_props.max_brightness = 255;
> +
> +	panel->backlight = devm_backlight_device_register(dev, dev_name(dev),
> +							  dev, dsi,
> +							  &rad_bl_ops,
> +							  &bl_props);
> +	if (IS_ERR(panel->backlight)) {
> +		ret = PTR_ERR(panel->backlight);
> +		dev_err(dev, "Failed to register backlight (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	drm_panel_init(&panel->base);
> +	panel->base.funcs = &rad_panel_funcs;
> +	panel->base.dev = dev;
> +	dev_set_drvdata(dev, panel);
> +
> +	ret = drm_panel_add(&panel->base);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0)
> +		drm_panel_remove(&panel->base);
> +
> +	return ret;
> +}
> +
> +static int rad_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	int ret;
> +
> +	ret = mipi_dsi_detach(dsi);
> +	if (ret < 0)
> +		DRM_DEV_ERROR(dev, "Failed to detach from host (%d)\n",
> +			      ret);
> +
> +	drm_panel_remove(&rad->base);
> +
> +	return 0;
> +}
> +
> +static void rad_panel_shutdown(struct mipi_dsi_device *dsi)
> +{
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +
> +	rad_panel_disable(&rad->base);
> +	rad_panel_unprepare(&rad->base);
> +}
> +
> +#ifdef CONFIG_PM
> +static int rad_panel_suspend(struct device *dev)
> +{
> +	struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +	if (!rad->reset)
> +		return 0;
> +
> +	devm_gpiod_put(dev, rad->reset);
> +	rad->reset = NULL;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_resume(struct device *dev)
> +{
> +	struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +	if (rad->reset)
> +		return 0;
> +
> +	rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(rad->reset))
> +		rad->reset = NULL;
> +
> +	return PTR_ERR_OR_ZERO(rad->reset);
> +}
> +
> +#endif
No other panels have suspend/resume.
Is it relevant?


> +
> +static const struct dev_pm_ops rad_pm_ops = {
> +	SET_RUNTIME_PM_OPS(rad_panel_suspend, rad_panel_resume, NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(rad_panel_suspend, rad_panel_resume)
> +};
> +
> +static const struct of_device_id rad_of_match[] = {
> +	{ .compatible = "raydium,rm67191", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rad_of_match);
> +
> +static struct mipi_dsi_driver rad_panel_driver = {
> +	.driver = {
> +		.name = "panel-raydium-rm67191",
> +		.of_match_table = rad_of_match,
> +		.pm	= &rad_pm_ops,
> +	},
> +	.probe = rad_panel_probe,
> +	.remove = rad_panel_remove,
> +	.shutdown = rad_panel_shutdown,
> +};
> +module_mipi_dsi_driver(rad_panel_driver);
> +
> +MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");
> +MODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI panel");
> +MODULE_LICENSE("GPL v2");


	Sam

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
@ 2019-06-14 15:10     ` Sam Ravnborg
  0 siblings, 0 replies; 15+ messages in thread
From: Sam Ravnborg @ 2019-06-14 15:10 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Mark Rutland, devicetree, David Airlie, linux-kernel, dri-devel,
	Rob Herring, Thierry Reding, linux-imx

Hi Robert.

On top of the feedback from Fabio here is a bit more.

> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.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 <video/of_videomode.h>
> +#include <video/videomode.h>

Divide include up in block in following order:

#include <linux/*>

#include <video/*>

#incude <drm/*>

Within each block sort alphabetically.
Do not use the deprecated drmP.h - replace it with the necessary
includes.
Use an empty line between each include block.

> +
> +/* Write Manufacture Command Set Control */
> +#define WRMAUCCTR 0xFE
> +
> +/* Manufacturer Command Set pages (CMD2) */
> +struct cmd_set_entry {
> +	u8 cmd;
> +	u8 param;
> +};
> +
> +/*
> + * There is no description in the Reference Manual about these commands.
> + * We received them from vendor, so just use them as is.
> + */
> +static const struct cmd_set_entry manufacturer_cmd_set[] = {
> +	{0xFE, 0x0B},
> +	{0x28, 0x40},
> +	{0x29, 0x4F},
...
> +	{0x51, 0x04},
> +};
> +
> +static const u32 rad_bus_formats[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_RGB666_1X18,
> +	MEDIA_BUS_FMT_RGB565_1X16,
> +};
> +
> +struct rad_panel {
> +	struct drm_panel base;
In the other raydium driver we name this "panel", which is a more
descriptive name.

> +	struct mipi_dsi_device *dsi;
> +
> +	struct gpio_desc *reset;
> +	struct backlight_device *backlight;
> +
> +	bool prepared;
> +	bool enabled;
> +
> +	struct videomode vm;
> +	u32 width_mm;
> +	u32 height_mm;
> +};
> +
> +static int rad_panel_prepare(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +
> +	if (rad->prepared)
> +		return 0;
> +
> +	if (rad->reset) {
> +		gpiod_set_value(rad->reset, 0);
> +		usleep_range(5000, 10000);
> +		gpiod_set_value(rad->reset, 1);
> +		usleep_range(20000, 25000);
> +	}
> +
> +	rad->prepared = true;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct device *dev = &rad->dsi->dev;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	if (rad->enabled) {
> +		DRM_DEV_ERROR(dev, "Panel still enabled!\n");
> +		return -EPERM;
> +	}
This seems like overkill, what should trigger this?

> +
> +	if (rad->reset) {
> +		gpiod_set_value(rad->reset, 0);
> +		usleep_range(15000, 17000);
> +		gpiod_set_value(rad->reset, 1);
> +	}
> +
> +	rad->prepared = false;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_enable(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct mipi_dsi_device *dsi = rad->dsi;
> +	struct device *dev = &dsi->dev;
> +	int color_format = color_format_from_dsi_format(dsi->format);
> +	u16 brightness;
> +	int ret;
> +
> +	if (rad->enabled)
> +		return 0;
> +
> +	if (!rad->prepared) {
> +		DRM_DEV_ERROR(dev, "Panel not prepared!\n");
> +		return -EPERM;
> +	}
Seems like overkill.

> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	ret = rad_panel_push_cmd_list(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to send MCS (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	/* Select User Command Set table (CMD1) */
> +	ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2);
> +	if (ret < 0)
> +		goto fail;
> +
> +	/* Software reset */
> +	ret = mipi_dsi_dcs_soft_reset(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to do Software Reset (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	usleep_range(15000, 17000);
> +
> +	/* Set DSI mode */
> +	ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set DSI mode (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set tear ON */
> +	ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set tear ON (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set tear scanline */
> +	ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set tear scanline (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set pixel format */
> +	ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format);
> +	DRM_DEV_DEBUG_DRIVER(dev, "Interface color format set to 0x%x\n",
> +			     color_format);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set pixel format (%d)\n", ret);
> +		goto fail;
> +	}
> +	/* Set display brightness */
> +	brightness = rad->backlight->props.brightness;
> +	ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display brightness (%d)\n",
> +			      ret);
> +		goto fail;
> +	}
brightness is written to again when you enable backlight below.
Seems redundant to do so twice.


> +	/* Exit sleep mode */
> +	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to exit sleep mode (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	usleep_range(5000, 10000);
> +
> +	ret = mipi_dsi_dcs_set_display_on(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display ON (%d)\n", ret);
> +		goto fail;
> +	}
> +
> +	backlight_enable(rad->backlight);
> +
> +	rad->enabled = true;
> +
> +	return 0;
> +
> +fail:
> +	if (rad->reset)
> +		gpiod_set_value(rad->reset, 0);
> +
> +	return ret;
> +}
> +
> +static int rad_panel_disable(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct mipi_dsi_device *dsi = rad->dsi;
> +	struct device *dev = &dsi->dev;
> +	int ret;
> +
> +	if (!rad->enabled)
> +		return 0;
> +
> +	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> +	backlight_disable(rad->backlight);
> +
> +	usleep_range(10000, 15000);
> +
> +	ret = mipi_dsi_dcs_set_display_off(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to set display OFF (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	usleep_range(5000, 10000);
> +
> +	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> +	if (ret < 0) {
> +		DRM_DEV_ERROR(dev, "Failed to enter sleep mode (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	rad->enabled = false;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_get_modes(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +	struct device *dev = &rad->dsi->dev;
> +	struct drm_connector *connector = panel->connector;
> +	struct drm_display_mode *mode;
> +	u32 *bus_flags = &connector->display_info.bus_flags;
> +	int ret;
> +
> +	mode = drm_mode_create(connector->dev);
> +	if (!mode) {
> +		DRM_DEV_ERROR(dev, "Failed to create display mode!\n");
> +		return 0;
> +	}
> +
> +	drm_display_mode_from_videomode(&rad->vm, mode);
> +	mode->width_mm = rad->width_mm;
> +	mode->height_mm = rad->height_mm;
> +	connector->display_info.width_mm = rad->width_mm;
> +	connector->display_info.height_mm = rad->height_mm;
> +	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +
> +	if (rad->vm.flags & DISPLAY_FLAGS_DE_HIGH)
> +		*bus_flags |= DRM_BUS_FLAG_DE_HIGH;
> +	if (rad->vm.flags & DISPLAY_FLAGS_DE_LOW)
> +		*bus_flags |= DRM_BUS_FLAG_DE_LOW;
> +	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
> +		*bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
> +	if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
> +		*bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
> +
> +	ret = drm_display_info_set_bus_formats(&connector->display_info,
> +					       rad_bus_formats,
> +					       ARRAY_SIZE(rad_bus_formats));
> +	if (ret)
> +		return ret;
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +static int rad_bl_get_brightness(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	u16 brightness;
> +	int ret;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	DRM_DEV_DEBUG_DRIVER(dev, "\n");
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	bl->props.brightness = brightness;
> +
> +	return brightness & 0xff;
> +}
> +
> +static int rad_bl_update_status(struct backlight_device *bl)
> +{
> +	struct mipi_dsi_device *dsi = bl_get_data(bl);
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	int ret = 0;
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	DRM_DEV_DEBUG_DRIVER(dev, "New brightness: %d\n", bl->props.brightness);
> +
> +	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> +	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static const struct backlight_ops rad_bl_ops = {
> +	.update_status = rad_bl_update_status,
> +	.get_brightness = rad_bl_get_brightness,
> +};
> +
> +static const struct drm_panel_funcs rad_panel_funcs = {
> +	.prepare = rad_panel_prepare,
> +	.unprepare = rad_panel_unprepare,
> +	.enable = rad_panel_enable,
> +	.disable = rad_panel_disable,
> +	.get_modes = rad_panel_get_modes,
> +};
> +
> +/*
> + * The clock might range from 66MHz (30Hz refresh rate)
> + * to 132MHz (60Hz refresh rate)
> + */
> +static const struct display_timing rad_default_timing = {
> +	.pixelclock = { 66000000, 132000000, 132000000 },
> +	.hactive = { 1080, 1080, 1080 },
> +	.hfront_porch = { 20, 20, 20 },
> +	.hsync_len = { 2, 2, 2 },
> +	.hback_porch = { 34, 34, 34 },
> +	.vactive = { 1920, 1920, 1920 },
> +	.vfront_porch = { 10, 10, 10 },
> +	.vsync_len = { 2, 2, 2 },
> +	.vback_porch = { 4, 4, 4 },
> +	.flags = DISPLAY_FLAGS_HSYNC_LOW |
> +		 DISPLAY_FLAGS_VSYNC_LOW |
> +		 DISPLAY_FLAGS_DE_LOW |
> +		 DISPLAY_FLAGS_PIXDATA_NEGEDGE,
> +};
> +
> +static int rad_panel_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *timings;
> +	struct rad_panel *panel;
> +	struct backlight_properties bl_props;
> +	int ret;
> +	u32 video_mode;
> +
> +	panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
> +	if (!panel)
> +		return -ENOMEM;
> +
> +	mipi_dsi_set_drvdata(dsi, panel);
> +
> +	panel->dsi = dsi;
> +
> +	dsi->format = MIPI_DSI_FMT_RGB888;
> +	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
> +			   MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> +	ret = of_property_read_u32(np, "video-mode", &video_mode);
> +	if (!ret) {
> +		switch (video_mode) {
> +		case 0:
> +			/* burst mode */
> +			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
> +			break;
> +		case 1:
> +			/* non-burst mode with sync event */
> +			break;
> +		case 2:
> +			/* non-burst mode with sync pulse */
> +			dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> +			break;
> +		default:
> +			dev_warn(dev, "invalid video mode %d\n", video_mode);
> +			break;
> +		}
> +	}
> +
> +	ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * 'display-timings' is optional, so verify if the node is present
> +	 * before calling of_get_videomode so we won't get console error
> +	 * messages
> +	 */
> +	timings = of_get_child_by_name(np, "display-timings");
> +	if (timings) {
> +		of_node_put(timings);
> +		ret = of_get_videomode(np, &panel->vm, 0);
> +	} else {
> +		videomode_from_timing(&rad_default_timing, &panel->vm);
> +	}
> +	if (ret < 0)
> +		return ret;
> +
> +	of_property_read_u32(np, "width-mm", &panel->width_mm);
> +	of_property_read_u32(np, "height-mm", &panel->height_mm);
> +
> +	panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> +
> +	if (IS_ERR(panel->reset))
> +		panel->reset = NULL;
> +	else
> +		gpiod_set_value(panel->reset, 0);
> +
> +	memset(&bl_props, 0, sizeof(bl_props));
> +	bl_props.type = BACKLIGHT_RAW;
> +	bl_props.brightness = 255;
> +	bl_props.max_brightness = 255;
> +
> +	panel->backlight = devm_backlight_device_register(dev, dev_name(dev),
> +							  dev, dsi,
> +							  &rad_bl_ops,
> +							  &bl_props);
> +	if (IS_ERR(panel->backlight)) {
> +		ret = PTR_ERR(panel->backlight);
> +		dev_err(dev, "Failed to register backlight (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	drm_panel_init(&panel->base);
> +	panel->base.funcs = &rad_panel_funcs;
> +	panel->base.dev = dev;
> +	dev_set_drvdata(dev, panel);
> +
> +	ret = drm_panel_add(&panel->base);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = mipi_dsi_attach(dsi);
> +	if (ret < 0)
> +		drm_panel_remove(&panel->base);
> +
> +	return ret;
> +}
> +
> +static int rad_panel_remove(struct mipi_dsi_device *dsi)
> +{
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +	struct device *dev = &dsi->dev;
> +	int ret;
> +
> +	ret = mipi_dsi_detach(dsi);
> +	if (ret < 0)
> +		DRM_DEV_ERROR(dev, "Failed to detach from host (%d)\n",
> +			      ret);
> +
> +	drm_panel_remove(&rad->base);
> +
> +	return 0;
> +}
> +
> +static void rad_panel_shutdown(struct mipi_dsi_device *dsi)
> +{
> +	struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> +
> +	rad_panel_disable(&rad->base);
> +	rad_panel_unprepare(&rad->base);
> +}
> +
> +#ifdef CONFIG_PM
> +static int rad_panel_suspend(struct device *dev)
> +{
> +	struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +	if (!rad->reset)
> +		return 0;
> +
> +	devm_gpiod_put(dev, rad->reset);
> +	rad->reset = NULL;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_resume(struct device *dev)
> +{
> +	struct rad_panel *rad = dev_get_drvdata(dev);
> +
> +	if (rad->reset)
> +		return 0;
> +
> +	rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(rad->reset))
> +		rad->reset = NULL;
> +
> +	return PTR_ERR_OR_ZERO(rad->reset);
> +}
> +
> +#endif
No other panels have suspend/resume.
Is it relevant?


> +
> +static const struct dev_pm_ops rad_pm_ops = {
> +	SET_RUNTIME_PM_OPS(rad_panel_suspend, rad_panel_resume, NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(rad_panel_suspend, rad_panel_resume)
> +};
> +
> +static const struct of_device_id rad_of_match[] = {
> +	{ .compatible = "raydium,rm67191", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rad_of_match);
> +
> +static struct mipi_dsi_driver rad_panel_driver = {
> +	.driver = {
> +		.name = "panel-raydium-rm67191",
> +		.of_match_table = rad_of_match,
> +		.pm	= &rad_pm_ops,
> +	},
> +	.probe = rad_panel_probe,
> +	.remove = rad_panel_remove,
> +	.shutdown = rad_panel_shutdown,
> +};
> +module_mipi_dsi_driver(rad_panel_driver);
> +
> +MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");
> +MODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI panel");
> +MODULE_LICENSE("GPL v2");


	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [EXT] Re: [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-14 15:10     ` Sam Ravnborg
  (?)
@ 2019-06-18  7:21     ` Robert Chiras
  -1 siblings, 0 replies; 15+ messages in thread
From: Robert Chiras @ 2019-06-18  7:21 UTC (permalink / raw)
  To: sam
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, daniel,
	mark.rutland, dri-devel, thierry.reding, airlied

Hi Sam,

Thank you for your suggestions.
See my reply inline.

On Vi, 2019-06-14 at 17:10 +0200, Sam Ravnborg wrote:
> Hi Robert.
> 
> On top of the feedback from Fabio here is a bit more.
> 
> > 
> > +
> > +#include <drm/drmP.h>
> > +#include <drm/drm_crtc.h>
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_panel.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 <video/of_videomode.h>
> > +#include <video/videomode.h>
> Divide include up in block in following order:
> 
> #include <linux/*>
> 
> #include <video/*>
> 
> #incude <drm/*>
> 
> Within each block sort alphabetically.
> Do not use the deprecated drmP.h - replace it with the necessary
> includes.
> Use an empty line between each include block.
Thanks. Will do.
> 
> > 
> > +
> > +/* Write Manufacture Command Set Control */
> > +#define WRMAUCCTR 0xFE
> > +
> > +/* Manufacturer Command Set pages (CMD2) */
> > +struct cmd_set_entry {
> > +     u8 cmd;
> > +     u8 param;
> > +};
> > +
> > +/*
> > + * There is no description in the Reference Manual about these
> > commands.
> > + * We received them from vendor, so just use them as is.
> > + */
> > +static const struct cmd_set_entry manufacturer_cmd_set[] = {
> > +     {0xFE, 0x0B},
> > +     {0x28, 0x40},
> > +     {0x29, 0x4F},
> ...
> > 
> > +     {0x51, 0x04},
> > +};
> > +
> > +static const u32 rad_bus_formats[] = {
> > +     MEDIA_BUS_FMT_RGB888_1X24,
> > +     MEDIA_BUS_FMT_RGB666_1X18,
> > +     MEDIA_BUS_FMT_RGB565_1X16,
> > +};
> > +
> > +struct rad_panel {
> > +     struct drm_panel base;
> In the other raydium driver we name this "panel", which is a more
> descriptive name.
> 
> > 
> > +     struct mipi_dsi_device *dsi;
> > +
> > +     struct gpio_desc *reset;
> > +     struct backlight_device *backlight;
> > +
> > +     bool prepared;
> > +     bool enabled;
> > +
> > +     struct videomode vm;
> > +     u32 width_mm;
> > +     u32 height_mm;
> > +};
> > +
> > +static int rad_panel_prepare(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +
> > +     if (rad->prepared)
> > +             return 0;
> > +
> > +     if (rad->reset) {
> > +             gpiod_set_value(rad->reset, 0);
> > +             usleep_range(5000, 10000);
> > +             gpiod_set_value(rad->reset, 1);
> > +             usleep_range(20000, 25000);
> > +     }
> > +
> > +     rad->prepared = true;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rad_panel_unprepare(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +     struct device *dev = &rad->dsi->dev;
> > +
> > +     if (!rad->prepared)
> > +             return 0;
> > +
> > +     if (rad->enabled) {
> > +             DRM_DEV_ERROR(dev, "Panel still enabled!\n");
> > +             return -EPERM;
> > +     }
> This seems like overkill, what should trigger this?
Probably, just bad written code in the DSI host driver, but you are
right, it's overkill. I will remove this.
> 
> > 
> > +
> > +     if (rad->reset) {
> > +             gpiod_set_value(rad->reset, 0);
> > +             usleep_range(15000, 17000);
> > +             gpiod_set_value(rad->reset, 1);
> > +     }
> > +
> > +     rad->prepared = false;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rad_panel_enable(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +     struct mipi_dsi_device *dsi = rad->dsi;
> > +     struct device *dev = &dsi->dev;
> > +     int color_format = color_format_from_dsi_format(dsi->format);
> > +     u16 brightness;
> > +     int ret;
> > +
> > +     if (rad->enabled)
> > +             return 0;
> > +
> > +     if (!rad->prepared) {
> > +             DRM_DEV_ERROR(dev, "Panel not prepared!\n");
> > +             return -EPERM;
> > +     }
> Seems like overkill.
> 
> > 
> > +
> > +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +     ret = rad_panel_push_cmd_list(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to send MCS (%d)\n", ret);
> > +             goto fail;
> > +     }
> > +
> > +     /* Select User Command Set table (CMD1) */
> > +     ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 },
> > 2);
> > +     if (ret < 0)
> > +             goto fail;
> > +
> > +     /* Software reset */
> > +     ret = mipi_dsi_dcs_soft_reset(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to do Software Reset
> > (%d)\n", ret);
> > +             goto fail;
> > +     }
> > +
> > +     usleep_range(15000, 17000);
> > +
> > +     /* Set DSI mode */
> > +     ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set DSI mode (%d)\n",
> > ret);
> > +             goto fail;
> > +     }
> > +     /* Set tear ON */
> > +     ret = mipi_dsi_dcs_set_tear_on(dsi,
> > MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set tear ON (%d)\n",
> > ret);
> > +             goto fail;
> > +     }
> > +     /* Set tear scanline */
> > +     ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set tear scanline
> > (%d)\n", ret);
> > +             goto fail;
> > +     }
> > +     /* Set pixel format */
> > +     ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format);
> > +     DRM_DEV_DEBUG_DRIVER(dev, "Interface color format set to
> > 0x%x\n",
> > +                          color_format);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set pixel format
> > (%d)\n", ret);
> > +             goto fail;
> > +     }
> > +     /* Set display brightness */
> > +     brightness = rad->backlight->props.brightness;
> > +     ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set display brightness
> > (%d)\n",
> > +                           ret);
> > +             goto fail;
> > +     }
> brightness is written to again when you enable backlight below.
> Seems redundant to do so twice.
I put this here from the initialization sequence received from the
panel vendor. But you are right, the backlight will be enabled later
on. I will remove this. 
> 
> 
> > 
> > +     /* Exit sleep mode */
> > +     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to exit sleep mode
> > (%d)\n", ret);
> > +             goto fail;
> > +     }
> > +
> > +     usleep_range(5000, 10000);
> > +
> > +     ret = mipi_dsi_dcs_set_display_on(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set display ON (%d)\n",
> > ret);
> > +             goto fail;
> > +     }
> > +
> > +     backlight_enable(rad->backlight);
> > +
> > +     rad->enabled = true;
> > +
> > +     return 0;
> > +
> > +fail:
> > +     if (rad->reset)
> > +             gpiod_set_value(rad->reset, 0);
> > +
> > +     return ret;
> > +}
> > +
> > +static int rad_panel_disable(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +     struct mipi_dsi_device *dsi = rad->dsi;
> > +     struct device *dev = &dsi->dev;
> > +     int ret;
> > +
> > +     if (!rad->enabled)
> > +             return 0;
> > +
> > +     dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +     backlight_disable(rad->backlight);
> > +
> > +     usleep_range(10000, 15000);
> > +
> > +     ret = mipi_dsi_dcs_set_display_off(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to set display OFF
> > (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     usleep_range(5000, 10000);
> > +
> > +     ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> > +     if (ret < 0) {
> > +             DRM_DEV_ERROR(dev, "Failed to enter sleep mode
> > (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     rad->enabled = false;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rad_panel_get_modes(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +     struct device *dev = &rad->dsi->dev;
> > +     struct drm_connector *connector = panel->connector;
> > +     struct drm_display_mode *mode;
> > +     u32 *bus_flags = &connector->display_info.bus_flags;
> > +     int ret;
> > +
> > +     mode = drm_mode_create(connector->dev);
> > +     if (!mode) {
> > +             DRM_DEV_ERROR(dev, "Failed to create display
> > mode!\n");
> > +             return 0;
> > +     }
> > +
> > +     drm_display_mode_from_videomode(&rad->vm, mode);
> > +     mode->width_mm = rad->width_mm;
> > +     mode->height_mm = rad->height_mm;
> > +     connector->display_info.width_mm = rad->width_mm;
> > +     connector->display_info.height_mm = rad->height_mm;
> > +     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> > +
> > +     if (rad->vm.flags & DISPLAY_FLAGS_DE_HIGH)
> > +             *bus_flags |= DRM_BUS_FLAG_DE_HIGH;
> > +     if (rad->vm.flags & DISPLAY_FLAGS_DE_LOW)
> > +             *bus_flags |= DRM_BUS_FLAG_DE_LOW;
> > +     if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
> > +             *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
> > +     if (rad->vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
> > +             *bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
> > +
> > +     ret = drm_display_info_set_bus_formats(&connector-
> > >display_info,
> > +                                            rad_bus_formats,
> > +                                            ARRAY_SIZE(rad_bus_for
> > mats));
> > +     if (ret)
> > +             return ret;
> > +
> > +     drm_mode_probed_add(panel->connector, mode);
> > +
> > +     return 1;
> > +}
> > +
> > +static int rad_bl_get_brightness(struct backlight_device *bl)
> > +{
> > +     struct mipi_dsi_device *dsi = bl_get_data(bl);
> > +     struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> > +     struct device *dev = &dsi->dev;
> > +     u16 brightness;
> > +     int ret;
> > +
> > +     if (!rad->prepared)
> > +             return 0;
> > +
> > +     DRM_DEV_DEBUG_DRIVER(dev, "\n");
> > +
> > +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > +     ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     bl->props.brightness = brightness;
> > +
> > +     return brightness & 0xff;
> > +}
> > +
> > +static int rad_bl_update_status(struct backlight_device *bl)
> > +{
> > +     struct mipi_dsi_device *dsi = bl_get_data(bl);
> > +     struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> > +     struct device *dev = &dsi->dev;
> > +     int ret = 0;
> > +
> > +     if (!rad->prepared)
> > +             return 0;
> > +
> > +     DRM_DEV_DEBUG_DRIVER(dev, "New brightness: %d\n", bl-
> > >props.brightness);
> > +
> > +     dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > +     ret = mipi_dsi_dcs_set_display_brightness(dsi, bl-
> > >props.brightness);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct backlight_ops rad_bl_ops = {
> > +     .update_status = rad_bl_update_status,
> > +     .get_brightness = rad_bl_get_brightness,
> > +};
> > +
> > +static const struct drm_panel_funcs rad_panel_funcs = {
> > +     .prepare = rad_panel_prepare,
> > +     .unprepare = rad_panel_unprepare,
> > +     .enable = rad_panel_enable,
> > +     .disable = rad_panel_disable,
> > +     .get_modes = rad_panel_get_modes,
> > +};
> > +
> > +/*
> > + * The clock might range from 66MHz (30Hz refresh rate)
> > + * to 132MHz (60Hz refresh rate)
> > + */
> > +static const struct display_timing rad_default_timing = {
> > +     .pixelclock = { 66000000, 132000000, 132000000 },
> > +     .hactive = { 1080, 1080, 1080 },
> > +     .hfront_porch = { 20, 20, 20 },
> > +     .hsync_len = { 2, 2, 2 },
> > +     .hback_porch = { 34, 34, 34 },
> > +     .vactive = { 1920, 1920, 1920 },
> > +     .vfront_porch = { 10, 10, 10 },
> > +     .vsync_len = { 2, 2, 2 },
> > +     .vback_porch = { 4, 4, 4 },
> > +     .flags = DISPLAY_FLAGS_HSYNC_LOW |
> > +              DISPLAY_FLAGS_VSYNC_LOW |
> > +              DISPLAY_FLAGS_DE_LOW |
> > +              DISPLAY_FLAGS_PIXDATA_NEGEDGE,
> > +};
> > +
> > +static int rad_panel_probe(struct mipi_dsi_device *dsi)
> > +{
> > +     struct device *dev = &dsi->dev;
> > +     struct device_node *np = dev->of_node;
> > +     struct device_node *timings;
> > +     struct rad_panel *panel;
> > +     struct backlight_properties bl_props;
> > +     int ret;
> > +     u32 video_mode;
> > +
> > +     panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
> > +     if (!panel)
> > +             return -ENOMEM;
> > +
> > +     mipi_dsi_set_drvdata(dsi, panel);
> > +
> > +     panel->dsi = dsi;
> > +
> > +     dsi->format = MIPI_DSI_FMT_RGB888;
> > +     dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE |
> > MIPI_DSI_MODE_VIDEO |
> > +                        MIPI_DSI_CLOCK_NON_CONTINUOUS;
> > +
> > +     ret = of_property_read_u32(np, "video-mode", &video_mode);
> > +     if (!ret) {
> > +             switch (video_mode) {
> > +             case 0:
> > +                     /* burst mode */
> > +                     dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
> > +                     break;
> > +             case 1:
> > +                     /* non-burst mode with sync event */
> > +                     break;
> > +             case 2:
> > +                     /* non-burst mode with sync pulse */
> > +                     dsi->mode_flags |=
> > MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> > +                     break;
> > +             default:
> > +                     dev_warn(dev, "invalid video mode %d\n",
> > video_mode);
> > +                     break;
> > +             }
> > +     }
> > +
> > +     ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes);
> > +     if (ret < 0) {
> > +             dev_err(dev, "Failed to get dsi-lanes property
> > (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     /*
> > +      * 'display-timings' is optional, so verify if the node is
> > present
> > +      * before calling of_get_videomode so we won't get console
> > error
> > +      * messages
> > +      */
> > +     timings = of_get_child_by_name(np, "display-timings");
> > +     if (timings) {
> > +             of_node_put(timings);
> > +             ret = of_get_videomode(np, &panel->vm, 0);
> > +     } else {
> > +             videomode_from_timing(&rad_default_timing, &panel-
> > >vm);
> > +     }
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     of_property_read_u32(np, "width-mm", &panel->width_mm);
> > +     of_property_read_u32(np, "height-mm", &panel->height_mm);
> > +
> > +     panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> > +
> > +     if (IS_ERR(panel->reset))
> > +             panel->reset = NULL;
> > +     else
> > +             gpiod_set_value(panel->reset, 0);
> > +
> > +     memset(&bl_props, 0, sizeof(bl_props));
> > +     bl_props.type = BACKLIGHT_RAW;
> > +     bl_props.brightness = 255;
> > +     bl_props.max_brightness = 255;
> > +
> > +     panel->backlight = devm_backlight_device_register(dev,
> > dev_name(dev),
> > +                                                       dev, dsi,
> > +                                                       &rad_bl_ops
> > ,
> > +                                                       &bl_props);
> > +     if (IS_ERR(panel->backlight)) {
> > +             ret = PTR_ERR(panel->backlight);
> > +             dev_err(dev, "Failed to register backlight (%d)\n",
> > ret);
> > +             return ret;
> > +     }
> > +
> > +     drm_panel_init(&panel->base);
> > +     panel->base.funcs = &rad_panel_funcs;
> > +     panel->base.dev = dev;
> > +     dev_set_drvdata(dev, panel);
> > +
> > +     ret = drm_panel_add(&panel->base);
> > +
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ret = mipi_dsi_attach(dsi);
> > +     if (ret < 0)
> > +             drm_panel_remove(&panel->base);
> > +
> > +     return ret;
> > +}
> > +
> > +static int rad_panel_remove(struct mipi_dsi_device *dsi)
> > +{
> > +     struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> > +     struct device *dev = &dsi->dev;
> > +     int ret;
> > +
> > +     ret = mipi_dsi_detach(dsi);
> > +     if (ret < 0)
> > +             DRM_DEV_ERROR(dev, "Failed to detach from host
> > (%d)\n",
> > +                           ret);
> > +
> > +     drm_panel_remove(&rad->base);
> > +
> > +     return 0;
> > +}
> > +
> > +static void rad_panel_shutdown(struct mipi_dsi_device *dsi)
> > +{
> > +     struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
> > +
> > +     rad_panel_disable(&rad->base);
> > +     rad_panel_unprepare(&rad->base);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int rad_panel_suspend(struct device *dev)
> > +{
> > +     struct rad_panel *rad = dev_get_drvdata(dev);
> > +
> > +     if (!rad->reset)
> > +             return 0;
> > +
> > +     devm_gpiod_put(dev, rad->reset);
> > +     rad->reset = NULL;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rad_panel_resume(struct device *dev)
> > +{
> > +     struct rad_panel *rad = dev_get_drvdata(dev);
> > +
> > +     if (rad->reset)
> > +             return 0;
> > +
> > +     rad->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> > +     if (IS_ERR(rad->reset))
> > +             rad->reset = NULL;
> > +
> > +     return PTR_ERR_OR_ZERO(rad->reset);
> > +}
> > +
> > +#endif
> No other panels have suspend/resume.
> Is it relevant?
This is because of the shared 'reset' pin with the touchscreen driver.
During suspend I need to release this gpio resource.
> 
> 
> > 
> > +
> > +static const struct dev_pm_ops rad_pm_ops = {
> > +     SET_RUNTIME_PM_OPS(rad_panel_suspend, rad_panel_resume, NULL)
> > +     SET_SYSTEM_SLEEP_PM_OPS(rad_panel_suspend, rad_panel_resume)
> > +};
> > +
> > +static const struct of_device_id rad_of_match[] = {
> > +     { .compatible = "raydium,rm67191", },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, rad_of_match);
> > +
> > +static struct mipi_dsi_driver rad_panel_driver = {
> > +     .driver = {
> > +             .name = "panel-raydium-rm67191",
> > +             .of_match_table = rad_of_match,
> > +             .pm     = &rad_pm_ops,
> > +     },
> > +     .probe = rad_panel_probe,
> > +     .remove = rad_panel_remove,
> > +     .shutdown = rad_panel_shutdown,
> > +};
> > +module_mipi_dsi_driver(rad_panel_driver);
> > +
> > +MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");
> > +MODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI
> > panel");
> > +MODULE_LICENSE("GPL v2");
> 
>         Sam

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2019-06-18  7:22 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-14 11:51 [PATCH 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
2019-06-14 11:51 ` [PATCH 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
2019-06-14 12:59   ` Fabio Estevam
2019-06-14 12:59     ` Fabio Estevam
2019-06-14 13:40     ` [EXT] " Robert Chiras
2019-06-14 11:51 ` [PATCH 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
2019-06-14 12:03   ` Daniel Baluta
2019-06-14 12:27   ` Fabio Estevam
2019-06-14 12:27     ` Fabio Estevam
2019-06-14 13:29     ` [EXT] " Robert Chiras
2019-06-14 13:39       ` Fabio Estevam
2019-06-14 13:50         ` Robert Chiras
2019-06-14 15:10   ` Sam Ravnborg
2019-06-14 15:10     ` Sam Ravnborg
2019-06-18  7:21     ` [EXT] " Robert Chiras

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.