dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Add DSI panel driver for Raydium RM67191
@ 2019-06-18 13:30 Robert Chiras
  2019-06-18 13:30 ` [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
  2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  0 siblings, 2 replies; 11+ messages in thread
From: Robert Chiras @ 2019-06-18 13:30 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.

Changes since v1:
- Fixed 'reset-gpio' to 'reset-gpios' property naming
- Changed the state of the reset gpio to active low and fixed how it is
  handled in driver
- Fixed copyright statement
- Reordered includes
- Added defines for panel specific color formats
- Removed unnecessary tests in enable and unprepare
- Removed the unnecessary backlight write in enable

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     |  43 ++
 drivers/gpu/drm/panel/Kconfig                      |   9 +
 drivers/gpu/drm/panel/Makefile                     |   1 +
 drivers/gpu/drm/panel/panel-raydium-rm67191.c      | 709 +++++++++++++++++++++
 4 files changed, 762 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] 11+ messages in thread

* [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-18 13:30 [PATCH v2 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
@ 2019-06-18 13:30 ` Robert Chiras
  2019-06-19 13:12   ` Sam Ravnborg
  2019-06-19 13:21   ` Fabio Estevam
  2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  1 sibling, 2 replies; 11+ messages in thread
From: Robert Chiras @ 2019-06-18 13:30 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     | 43 ++++++++++++++++++++++
 1 file changed, 43 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..0952610
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
@@ -0,0 +1,43 @@
+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-gpios:		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/panel/display-timing.txt
+
+Example:
+
+	panel@0 {
+		compatible = "raydium,rm67191";
+		reg = <0>;
+		pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;
+		pinctrl-names = "default";
+		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+		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] 11+ messages in thread

* [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-18 13:30 [PATCH v2 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
  2019-06-18 13:30 ` [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
@ 2019-06-18 13:30 ` Robert Chiras
  2019-06-19 13:25   ` Sam Ravnborg
                     ` (2 more replies)
  1 sibling, 3 replies; 11+ messages in thread
From: Robert Chiras @ 2019-06-18 13:30 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 | 709 ++++++++++++++++++++++++++
 3 files changed, 719 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..c0780c9
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
@@ -0,0 +1,709 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX drm driver - Raydium MIPI-DSI panel driver
+ *
+ * Copyright 2019 NXP
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+/* Write Manufacture Command Set Control */
+#define WRMAUCCTR 0xFE
+
+#define COL_FMT_16BPP 0x55
+#define COL_FMT_18BPP 0x66
+#define COL_FMT_24BPP 0x77
+
+/* 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 panel;
+	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, panel);
+}
+
+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 COL_FMT_16BPP;
+	case MIPI_DSI_FMT_RGB666:
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		return COL_FMT_18BPP;
+	case MIPI_DSI_FMT_RGB888:
+		return COL_FMT_24BPP;
+	default:
+		return COL_FMT_24BPP; /* 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_cansleep(rad->reset, 1);
+		usleep_range(3000, 5000);
+		gpiod_set_value_cansleep(rad->reset, 0);
+		usleep_range(18000, 20000);
+	}
+
+	rad->prepared = true;
+
+	return 0;
+}
+
+static int rad_panel_unprepare(struct drm_panel *panel)
+{
+	struct rad_panel *rad = to_rad_panel(panel);
+
+	if (!rad->prepared)
+		return 0;
+
+	if (rad->reset) {
+		gpiod_set_value_cansleep(rad->reset, 1);
+		usleep_range(15000, 17000);
+		gpiod_set_value_cansleep(rad->reset, 0);
+	}
+
+	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);
+	int ret;
+
+	if (rad->enabled)
+		return 0;
+
+	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;
+	}
+	/* 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, 7000);
+
+	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_cansleep(rad->reset, 1);
+
+	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, 12000);
+
+	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_LOW);
+
+	if (IS_ERR(panel->reset))
+		panel->reset = NULL;
+	else
+		gpiod_set_value_cansleep(panel->reset, 1);
+
+	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->panel);
+	panel->panel.funcs = &rad_panel_funcs;
+	panel->panel.dev = dev;
+	dev_set_drvdata(dev, panel);
+
+	ret = drm_panel_add(&panel->panel);
+
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0)
+		drm_panel_remove(&panel->panel);
+
+	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->panel);
+
+	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->panel);
+	rad_panel_unprepare(&rad->panel);
+}
+
+#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_LOW);
+	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] 11+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-18 13:30 ` [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
@ 2019-06-19 13:12   ` Sam Ravnborg
  2019-06-19 13:21   ` Fabio Estevam
  1 sibling, 0 replies; 11+ messages in thread
From: Sam Ravnborg @ 2019-06-19 13:12 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Mark Rutland, devicetree, David Airlie, linux-kernel, dri-devel,
	Rob Herring, Thierry Reding, linux-imx

Hi Robert.

Thanks for the contribution.

On Tue, Jun 18, 2019 at 04:30:45PM +0300, Robert Chiras 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     | 43 ++++++++++++++++++++++
>  1 file changed, 43 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..0952610
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.txt
> @@ -0,0 +1,43 @@
> +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-gpios:		a GPIO spec for the RST_B GPIO pin
> +- pinctrl-0		phandle to the pin settings for the reset pin
pinctrl is not included in bindings, they are implicit.

> +- width-mm:		physical panel width [mm]
> +- height-mm:		physical panel height [mm]
Please refer to panel-common.txt for the above.

> +- 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/panel/display-timing.txt
> +
> +Example:
> +
> +	panel@0 {
> +		compatible = "raydium,rm67191";
> +		reg = <0>;
> +		pinctrl-0 = <&pinctrl_mipi_dsi_0_1_en>;
> +		pinctrl-names = "default";
> +		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
> +		dsi-lanes = <4>;
> +		width-mm = <68>;
> +		height-mm = <121>;
> +
> +		port {
> +			panel_in: endpoint {
> +				remote-endpoint = <&mipi_out>;
> +			};
> +		};
> +	};

With the above fixed:
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>

Note: You need r-b from DT maintainer before we can apply it.

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

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

* Re: [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel
  2019-06-18 13:30 ` [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
  2019-06-19 13:12   ` Sam Ravnborg
@ 2019-06-19 13:21   ` Fabio Estevam
  2019-06-20  7:58     ` [EXT] " Robert Chiras
  1 sibling, 1 reply; 11+ messages in thread
From: Fabio Estevam @ 2019-06-19 13:21 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 Tue, Jun 18, 2019 at 10:33 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> +Optional properties:
> +- reset-gpios:         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]

Still not convinced we need the 'display-timings' property, even as an
optional property. My understanding is that passing display timings in
the devicetree is not encouraged.

Last time you said you need to pass ''display-timings' to workaround
the problem of connecting this panel to mx8m DCSS or eLCDIF.

The panel timings come from the LCD manufacturer and it is agnostic to
what display controller interface it is connected to.

So I suggest making sure the timings passed in the driver are correct
as per the vendor datasheet. If they are correct and one specific
interface is not able to drive it, then probably it is a bug in this
specific display controller interface or in the SoC clock driver.

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

* Re: [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
@ 2019-06-19 13:25   ` Sam Ravnborg
  2019-06-20  8:30     ` [EXT] " Robert Chiras
  2019-06-19 13:28   ` Fabio Estevam
  2019-06-19 14:06   ` Fabio Estevam
  2 siblings, 1 reply; 11+ messages in thread
From: Sam Ravnborg @ 2019-06-19 13:25 UTC (permalink / raw)
  To: Robert Chiras
  Cc: Mark Rutland, devicetree, David Airlie, linux-kernel, dri-devel,
	Rob Herring, Thierry Reding, linux-imx

On Tue, Jun 18, 2019 at 04:30:46PM +0300, Robert Chiras wrote:
> This patch adds Raydium RM67191 TFT LCD panel driver (MIPI-DSI
> protocol).
> 
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
Please include in the changelog a list of what was updated - like this:

v2:
- added kconif symbol sorted (sam)
- another nitpick (foo)
- etc

In general try to namme who gave feedback to give them some credit.

Who is maintainer for this onwards?

> ---
>  drivers/gpu/drm/panel/Kconfig                 |   9 +
>  drivers/gpu/drm/panel/Makefile                |   1 +
>  drivers/gpu/drm/panel/panel-raydium-rm67191.c | 709 ++++++++++++++++++++++++++
>  3 files changed, 719 insertions(+)
>  create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c
> 
> +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_cansleep(rad->reset, 1);
> +		usleep_range(3000, 5000);
> +		gpiod_set_value_cansleep(rad->reset, 0);

So writing a 0 will release reset.
> +		usleep_range(18000, 20000);
> +	}
> +
> +	rad->prepared = true;
> +
> +	return 0;
> +}
> +
> +static int rad_panel_unprepare(struct drm_panel *panel)
> +{
> +	struct rad_panel *rad = to_rad_panel(panel);
> +
> +	if (!rad->prepared)
> +		return 0;
> +
> +	if (rad->reset) {
> +		gpiod_set_value_cansleep(rad->reset, 1);
> +		usleep_range(15000, 17000);
> +		gpiod_set_value_cansleep(rad->reset, 0);
Looks strange that reset is released in unprepare.
I would expect it to stay reset to minimize power etc.

> +
> +	ret = drm_display_info_set_bus_formats(&connector->display_info,
> +					       rad_bus_formats,
> +					       ARRAY_SIZE(rad_bus_formats));

Other drivers has this as the last stement in their enable function.
I did not check why, but maybe something to invest a few minutes into.
Be different only if there is a reason to do so.

> +	if (ret)
> +		return ret;
> +
> +	drm_mode_probed_add(panel->connector, mode);
> +
> +	return 1;
> +}
> +
> +
> +#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_LOW);
> +	if (IS_ERR(rad->reset))
> +		rad->reset = NULL;
> +
> +	return PTR_ERR_OR_ZERO(rad->reset);
> +}
> +
> +#endif

Use __maybe_unused for the tw functions above.
And loose the ifdef...

> +
> +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", },
> +	{ }
We often, but not always, write this as { /* sentinal */ },

> +};
> +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");

With the above trivialities considered/fixed:
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

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

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

* Re: [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  2019-06-19 13:25   ` Sam Ravnborg
@ 2019-06-19 13:28   ` Fabio Estevam
  2019-06-20  8:34     ` [EXT] " Robert Chiras
  2019-06-19 14:06   ` Fabio Estevam
  2 siblings, 1 reply; 11+ messages in thread
From: Fabio Estevam @ 2019-06-19 13:28 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 Tue, Jun 18, 2019 at 10:31 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> +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 },

Are you sure that the sync_len and porch parameters are the same for
both 66MHz and 132MHz?

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

* Re: [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
  2019-06-19 13:25   ` Sam Ravnborg
  2019-06-19 13:28   ` Fabio Estevam
@ 2019-06-19 14:06   ` Fabio Estevam
  2 siblings, 0 replies; 11+ messages in thread
From: Fabio Estevam @ 2019-06-19 14:06 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

On Tue, Jun 18, 2019 at 10:31 AM Robert Chiras <robert.chiras@nxp.com> wrote:

> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
> @@ -0,0 +1,709 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * i.MX drm driver - Raydium MIPI-DSI panel driver

Please remove the "i.MX drm driver" as there is nothing i.MX specific
in this driver.


> + *
> + * Copyright 2019 NXP
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/mipi_display.h>
> +#include <video/of_videomode.h>
> +#include <video/videomode.h>
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +
> +/* Write Manufacture Command Set Control */
> +#define WRMAUCCTR 0xFE
> +
> +#define COL_FMT_16BPP 0x55
> +#define COL_FMT_18BPP 0x66
> +#define COL_FMT_24BPP 0x77
> +
> +/* 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 panel;
> +       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, panel);
> +}
> +
> +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 COL_FMT_16BPP;
> +       case MIPI_DSI_FMT_RGB666:
> +       case MIPI_DSI_FMT_RGB666_PACKED:
> +               return COL_FMT_18BPP;
> +       case MIPI_DSI_FMT_RGB888:
> +               return COL_FMT_24BPP;
> +       default:
> +               return COL_FMT_24BPP; /* 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_cansleep(rad->reset, 1);
> +               usleep_range(3000, 5000);
> +               gpiod_set_value_cansleep(rad->reset, 0);
> +               usleep_range(18000, 20000);
> +       }
> +
> +       rad->prepared = true;
> +
> +       return 0;
> +}
> +
> +static int rad_panel_unprepare(struct drm_panel *panel)
> +{
> +       struct rad_panel *rad = to_rad_panel(panel);
> +
> +       if (!rad->prepared)
> +               return 0;
> +
> +       if (rad->reset) {
> +               gpiod_set_value_cansleep(rad->reset, 1);
> +               usleep_range(15000, 17000);
> +               gpiod_set_value_cansleep(rad->reset, 0);
> +       }
> +
> +       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);
> +       int ret;
> +
> +       if (rad->enabled)
> +               return 0;
> +
> +       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;
> +       }
> +       /* 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, 7000);
> +
> +       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_cansleep(rad->reset, 1);
> +
> +       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, 12000);
> +
> +       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_LOW);
> +
> +       if (IS_ERR(panel->reset))
> +               panel->reset = NULL;
> +       else
> +               gpiod_set_value_cansleep(panel->reset, 1);
> +
> +       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->panel);
> +       panel->panel.funcs = &rad_panel_funcs;
> +       panel->panel.dev = dev;
> +       dev_set_drvdata(dev, panel);
> +
> +       ret = drm_panel_add(&panel->panel);
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = mipi_dsi_attach(dsi);
> +       if (ret < 0)
> +               drm_panel_remove(&panel->panel);
> +
> +       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->panel);
> +
> +       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->panel);
> +       rad_panel_unprepare(&rad->panel);
> +}
> +
> +#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_LOW);
> +       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
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

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

On Mi, 2019-06-19 at 10:21 -0300, Fabio Estevam wrote:
> Caution: EXT Email
> 
> Hi Robert,
> 
> On Tue, Jun 18, 2019 at 10:33 AM Robert Chiras <robert.chiras@nxp.com
> > wrote:
> 
> > 
> > +Optional properties:
> > +- reset-gpios:         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]
> Still not convinced we need the 'display-timings' property, even as
> an
> optional property. My understanding is that passing display timings
> in
> the devicetree is not encouraged.
> 
> Last time you said you need to pass ''display-timings' to workaround
> the problem of connecting this panel to mx8m DCSS or eLCDIF.
> 
> The panel timings come from the LCD manufacturer and it is agnostic
> to
> what display controller interface it is connected to.
> 
> So I suggest making sure the timings passed in the driver are correct
> as per the vendor datasheet. If they are correct and one specific
> interface is not able to drive it, then probably it is a bug in this
> specific display controller interface or in the SoC clock driver.

I understand. I will remove the display-timings from dt and also from
driver. Currently, this panel works in this form with both LCDIF and
DCSS on our 8M SoC so, as you said, probably the issue we were seeing
in our tree was coming from elsewhere.

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

* Re: [EXT] Re: [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver
  2019-06-19 13:25   ` Sam Ravnborg
@ 2019-06-20  8:30     ` Robert Chiras
  0 siblings, 0 replies; 11+ messages in thread
From: Robert Chiras @ 2019-06-20  8:30 UTC (permalink / raw)
  To: sam
  Cc: dl-linux-imx, linux-kernel, robh+dt, devicetree, daniel,
	mark.rutland, dri-devel, thierry.reding, airlied

Hi Sam,

On Mi, 2019-06-19 at 15:25 +0200, Sam Ravnborg wrote:
> On Tue, Jun 18, 2019 at 04:30:46PM +0300, Robert Chiras wrote:
> > 
> > This patch adds Raydium RM67191 TFT LCD panel driver (MIPI-DSI
> > protocol).
> > 
> > Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> Please include in the changelog a list of what was updated - like
> this:
> 
> v2:
> - added kconif symbol sorted (sam)
> - another nitpick (foo)
> - etc
> 
> In general try to namme who gave feedback to give them some credit.
Thanks. I will keep this in mind, next time
> 
> Who is maintainer for this onwards?
I can offer myself to maintain this.
> 
> > 
> > ---
> >  drivers/gpu/drm/panel/Kconfig                 |   9 +
> >  drivers/gpu/drm/panel/Makefile                |   1 +
> >  drivers/gpu/drm/panel/panel-raydium-rm67191.c | 709
> > ++++++++++++++++++++++++++
> >  3 files changed, 719 insertions(+)
> >  create mode 100644 drivers/gpu/drm/panel/panel-raydium-rm67191.c
> > 
> > +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_cansleep(rad->reset, 1);
> > +             usleep_range(3000, 5000);
> > +             gpiod_set_value_cansleep(rad->reset, 0);
> So writing a 0 will release reset.
> > 
> > +             usleep_range(18000, 20000);
> > +     }
> > +
> > +     rad->prepared = true;
> > +
> > +     return 0;
> > +}
> > +
> > +static int rad_panel_unprepare(struct drm_panel *panel)
> > +{
> > +     struct rad_panel *rad = to_rad_panel(panel);
> > +
> > +     if (!rad->prepared)
> > +             return 0;
> > +
> > +     if (rad->reset) {
> > +             gpiod_set_value_cansleep(rad->reset, 1);
> > +             usleep_range(15000, 17000);
> > +             gpiod_set_value_cansleep(rad->reset, 0);
> Looks strange that reset is released in unprepare.
> I would expect it to stay reset to minimize power etc.
Yes, it looks strange indeed. The reason here is coming from the fact
that this panel also has touch-screen that shares this reset pin with
the OLED display. While the display may be turned off, the touch driver
may need to keep the connection active with the touch controller from
this panel. This is the reason for releasing the reset pin in
unprepare. I will add this in a comment in the code, to eliminate the
confusion.
> 
> > 
> > +
> > +     ret = drm_display_info_set_bus_formats(&connector-
> > >display_info,
> > +                                            rad_bus_formats,
> > +                                            ARRAY_SIZE(rad_bus_for
> > mats));
> Other drivers has this as the last stement in their enable function.
> I did not check why, but maybe something to invest a few minutes
> into.
> Be different only if there is a reason to do so.
Ok, I will move this as the last statement. I also noticed that the
other panel drivers do not check the return of this call, like I do
here, so I will also remove this too.
> 
> > 
> > +     if (ret)
> > +             return ret;
> > +
> > +     drm_mode_probed_add(panel->connector, mode);
> > +
> > +     return 1;
> > +}
> > +
> > +
> > +#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_LOW);
> > +     if (IS_ERR(rad->reset))
> > +             rad->reset = NULL;
> > +
> > +     return PTR_ERR_OR_ZERO(rad->reset);
> > +}
> > +
> > +#endif
> Use __maybe_unused for the tw functions above.
> And loose the ifdef...
Thanks. Will apply.
> 
> > 
> > +
> > +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", },
> > +     { }
> We often, but not always, write this as { /* sentinal */ },
Thanks. Will apply.
> 
> > 
> > +};
> > +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");
> With the above trivialities considered/fixed:
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
> 
>         Sam

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

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

On Mi, 2019-06-19 at 10:28 -0300, Fabio Estevam wrote:
> Caution: EXT Email
> 
> Hi Robert,
> 
> On Tue, Jun 18, 2019 at 10:31 AM Robert Chiras <robert.chiras@nxp.com
> > wrote:
> 
> > 
> > +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 },
> Are you sure that the sync_len and porch parameters are the same for
> both 66MHz and 132MHz?
Probably they are not, since I didn't get this panel to work well with
66Hz. So I will just keep 132MHz, since these are the only timinings we
received from vendor.

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

end of thread, other threads:[~2019-06-20  8:34 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-18 13:30 [PATCH v2 0/2] Add DSI panel driver for Raydium RM67191 Robert Chiras
2019-06-18 13:30 ` [PATCH v2 1/2] dt-bindings: display: panel: Add support for Raydium RM67191 panel Robert Chiras
2019-06-19 13:12   ` Sam Ravnborg
2019-06-19 13:21   ` Fabio Estevam
2019-06-20  7:58     ` [EXT] " Robert Chiras
2019-06-18 13:30 ` [PATCH v2 2/2] drm/panel: Add support for Raydium RM67191 panel driver Robert Chiras
2019-06-19 13:25   ` Sam Ravnborg
2019-06-20  8:30     ` [EXT] " Robert Chiras
2019-06-19 13:28   ` Fabio Estevam
2019-06-20  8:34     ` [EXT] " Robert Chiras
2019-06-19 14:06   ` Fabio Estevam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).